Google Map Hack

来自Jack's Lab
2014年4月24日 (四) 15:03Comcat (讨论 | 贡献)的版本

(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到: 导航, 搜索

注意:以下资料皆来自网络,未经验证

目录

1 概述

Google 的地图采用将地球圆表面投影成平面的方式进行贴图

假设 zoom = 18,则横坐标从左至右像素为 0 - 256*2^18(2的18次幂),也就是每增加一级,地图大小横纵坐标加倍,256为一个标准图片的大小。显示-180度到+180的范围,经度越大,x 越大

纵坐标从上到下像素为 0 - 256*2^18(2的18次幂)。显示+85到-85度的范围,纬度越小,y 越大


经度到像素 X 值:

 (long + 180) * (256L << zoom) / 360

像素 X 到经度:

 X * 360 / (256L << zoom) - 180


纬度到像素 Y:

double siny = Math.sin(lat * Math.PI / 180);
double y = Math.log((1 + siny) / (1 - siny));
Y = (128 << zoom) * (1 - y / (2 * Math.PI));

像素Y到纬度:

double y = 2 * Math.PI * (1 - Y / (128 << zoom));
double z = Math.pow(Math.E, y);
double siny = (z - 1) / (z + 1);
Lat = Math.asin(siny) * 180 / Math.PI;



2 矫正

http://wenku.baidu.com/view/01b8af8ca0116c175f0e4866.html

http://wenku.baidu.com/view/d27fd5bc1a37f111f1855b1c.html

http://wenku.baidu.com/view/808cbcd549649b6648d7470b.html

http://blog.sina.com.cn/s/blog_538036cf0100pxbl.html



3 Google Map Tile 下载

坐标到Tile的计算参考:http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/

#coding=utf-8
#Filename:dmc.py
import cmath,urllib,os,math,random,sys,time,threading

class Cdt:
    def __str__(self):
        return '{x = %s,y = %s}'%(self.x,self.y)        

def createName(num,type):
    #'''创建文件夹/文件名称'''
    temp = '00000000'+str(hex(int(num)))[2:]
    return type + temp[::-1][0:8][::-1]

def getPixelFromCdt(x,y,z):
    #'''根据经纬度坐标以及缩放等级获取像素坐标'''
    pixel = Cdt()
    sinLatitude = cmath.sin(y * cmath.pi / 180)
    pixel.x = ((x + 180) / 360) * 256 * (2**z)
    temp = cmath.log((1+sinLatitude)/(1-sinLatitude))
    pixel.y = abs((0.5 - temp / (4 * cmath.pi))*256*(2**z))
    return pixel

def getTileFromPixel(pixel):
    #'''根据像素坐标获取切片'''
    tile = Cdt()
    tile.x = math.floor(pixel.x / 256)
    tile.y = math.floor(pixel.y / 256)
    return tile

def getTileFromCdt(x,y,z):
    #'''根据经纬度坐标以及缩放等级获取切片'''
    return getTileFromPixel(getPixelFromCdt(x,y,z))

def downloadTile(remoteFile,localFile):
    #'''下载远程文件到本地'''
    urllib.urlretrieve(remoteFile,localFile)

def createDir(dirPath):
    #'''创建文件夹'''
    if not os.path.exists(dirPath):
        os.makedirs(dirPath)

def createRemoteUrl(x,y,z):
    #'''创建远程tile地址'''
    port = str(random.randint(0,3))
    x = str(x)
    y = str(y)
    z = str(z)
    return 'http://mt'+port+'.google.cn/vt/v=w2.115&hl=zh-CN&gl=cn&x='+x+'&s=&y='+y+'&z='+z

def createLocalFile(x,y,z,lvRange,cacheDir):
    #'''创建缓存本地路径'''
    #计算<等级目录>名称
    i = int(z) - lvRange[0]
    l = ''
    if i < 10:
        l = 'L0' + str(i)
    elif i >= 10 and i <= lvRange[len(lvRange) - 1]:
        l = 'L' + str(i)
    #计算<行目录>和<(列)图片>名称
    r = createName(y,'R')
    c = createName(x,'C')
    #拼装本地路径
    return cacheDir + os.sep + l + os.sep + r + os.sep + c + '.png'

def createCacheDir(rowName,lv,row,xRange):
    #'''创建缓存目录'''
    createDir(rowName)#创建行文件夹
    for col in xRange:
        #localTile = rowName + os.sep + createName(col,'C') + '.png'
        tempTask.append('%s,%s,%s'%(lv,row,col))
        
def createLvCache(extent,lv,dir):
    #'''创建某一等级下一行cache'''
    startTile = getTileFromCdt(extent[0],extent[1],lv)
    endTile = getTileFromCdt(extent[2],extent[3],lv)
    xRange = range(int(startTile.x),int(endTile.x))
    yRange = range(int(startTile.y),int(endTile.y))
    for row in yRange:
        rowName = dir + os.sep + createName(row,'R')     
        createCacheDir(rowName,lv,row,xRange)
   
def createCacheStruc(extent,lvRange,cacheDir):
    #'''创建缓存目录结构及计算tile'''
    global tempTask
    tempTask=[]#存储cache下载列表
    count = 0
    print '创建Cache目录及计算tile数目...'
    for lv in lvRange:        
        lvName = 'L0' + str(count)#lvName:等级文件夹名        
        createDir(cacheDir +os.sep + lvName)#创建lv等级的文件夹,例如:lv01,lv02...
        createLvCache(extent,lv,cacheDir +os.sep + lvName)#创建完等级文件夹后,添加该等级的行文件夹,例如:R000001a0
        count += 1

def loadErrorFile(url):
    '''
    加载错误任务
    '''
    os.rename(url,url+'bak')
    f = open(url+'bak')
    return f.readlines()
    

def loadTask(task,threadNum):
    global failureTask
    failureTask = []
    tasksize = len(task)
    threadTask = tasksize//int(threadNum)       #取整除 返回商的整数部分
    print '待下载Tile总计:%s,下载线程数:%s'%(tasksize,sys.argv[5])
    
    for i in range(len(task)):
        #log('开启线程:%s'%i,False)
        st = i * int(threadTask)
        ed = st + int(threadTask) + 1
        if ed > len(task) - 1:
            download = Download(task[st:len(task) - 1])
            download.start()
            break
        download = Download(task[st:ed])
        download.start()
        time.sleep(0.5)   
    
      
class Download(threading.Thread):
    sucessCount = 0
    failureCount = 0
    def __init__(self,tasks):
        threading.Thread.__init__(self)
        self.tasks = tasks
        self.lock = threading.RLock()
    def run(self):        
        #log('start download',False)
        for task in self.tasks:
            valueAry = task.split(",")
            lv = valueAry[0]
            row = valueAry[1]
            col = valueAry[2]
            remoteFile = createRemoteUrl(col,row,lv)
            localFile = createLocalFile(col,row,lv,lvRange,cacheDir)
            try:
                downloadTile(remoteFile,localFile)
                self.lock.acquire()
                Download.sucessCount += 1
                log('已经下载:%s / %s ,失败: %s / %s'%(Download.sucessCount,len(tempTask),Download.failureCount,len(failureTask)),False)
                self.lock.release()
            except:
                logStr = '%s,%s,%s'%(lv,row,col)
                f = file(cacheDir+os.sep+'error.log','a')#在日志文件中打印失败记录
                f.write(logStr+'\n')
                f.close()
                self.lock.acquire()
                Download.failureCount += 1
                self.lock.release()
                failureTask.append(logStr)
            
def log(event,b):
    #'''打印消息或日志'''
    try:
        logStr = str(event)
        if b:#在日志文件中打印消息
            f = file(cacheDir+os.sep+str(time.strftime('%Y%m%d%H'))+'.log','a')
            f.write(logStr+'\n')
            f.close()
        sys.stdout.write('\r'+logStr)#在控制台中打印消息
        sys.stdout.flush()
    except:
        pass


if __name__ == '__main__':
    
    extent = sys.argv[1]
    maxLv = sys.argv[2]
    minLv = sys.argv[3]

    global extAry,lvRange,cacheDir,threadNum
    ext = [float(i) for i in extent.split(' ')]
    extAry = [ext[0],ext[3],ext[2],ext[1]]          #区域范围
    lvRange = range(int(minLv),int(maxLv) + 1)      #等级范围
    cacheDir = sys.argv[4]                          #下载目录
    threadNum = sys.argv[5]                         #下载线程

    if sys.argv[1] == 'loadError':
        errorTask = loadErrorFile(sys.argv[2]+os.sep+'error.log')
        loadTask(tempTask,sys.argv[3])  
    else:
        createCacheStruc(extAry,lvRange,cacheDir)       #创建缓存目录结构
        loadTask(tempTask,threadNum)                    #下载


示例:

$ ./dmc.py "118.411792548134 31.5176549981089 119.43094617187 32.3386680868215" 16 10 /gmap/data 20

"118.411792548134 31.5176549981089 119.43094617187 32.3386680868215" 是待下载的区域范围

16 10 是下载的等级范围

/gmap/data 是存储目录

20 是线程数



4 World File Format

http://en.wikipedia.org/wiki/World_file

Subject 	World File Format 
Author 	A.J. Romanelli 
Date 	Apr 10, 2006 
Message 	The units of a world file are map units. For example, if the x/y values you enter are state-plane coordinates in feet, then your image will be referenced to state-plane feet, if the values you enter are utm meters, then your image will be referenced to that UTM zone. The values for the offsets and cell size need to be specified in the same units (e.g. if you are in state-plane use feet, if you are working in UTM use meters). 

From the MO help.... 

Vector data in MapLayers exists in a real-world or map coordinate system, measured, typically, in feet or meters. The x-coordinates increase from left to right and the y-coordinates increase from the bottom to the top. This is quite different from a raster image represented by an ImageLayer. A raster image is organized and measured by rows and columns. Each cell has a row number and a column number. If the origin is located in the upper left corner of the data, that cell would be identified as row 1, column 1. 

For MapLayers and ImageLayers to be displayed simultaneously, the rows and columns of the image must be mapped into the x,y plane of a map coordinate system. An image-to-world transformation that converts the image coordinates to map coordinates must be established. Some image formats store georeferencing information in the file header of the image or, in the case of images that do not contain this georeferencing information, facilities exist in other products available from ESRI, for creating a file that contains the necessary transformation parameters. The file that contains the transformation parameters is called a world file. The world file takes precedence over any header information. 

About the world file 

The image-to-world transformation is a six parameter affine transformation of the form: 

x' = Ax + By + C 
y' = Dx + Ey + F 

where 

x' = calculated x-coordinate of the pixel on the map 
y' = calculated y-coordinate of the pixel on the map 
x = column number of a pixel in the image 
y = row number of a pixel in the image 
A = x-scale, dimension of a pixel in map units in the x-direction 
D,B = rotation terms. Note: Not supported for this release. 
E = y-scale (this value is always negative, because image space is top-down, whereas map space is bottom-up) 
C = translation term; x-Origin (x-coordinate of the center of the upper left pixel) 
F = translation term; y-Origin (y-coordinate of the center of the upper left pixel) 
The transformation parameters are stored in the world file, an ASCII format, in this order, A, D, B, E, C, F; for example: 

2.22123393184959 A 
0.00000000000000 D 
0.00000000000000 B 
-2.22123393184959 E 
10383.13600759092515 C 
11611.48117990907485 F 
Note that the parameter characters are included in the example for clarity. They do not actually appear in the file. 
If a world file is not present and there is no georeferencing information in the header of the image, a default mapping is still provided between image space and map space. MapObjects makes the origin of the image (-0.5, -0.5), and sets the X and Y scale factors both to 1.0 (as the center of the bottom right pixel should be at (0,0). 
If you want to display a non-georeferenced image on a portion of your map, supply the georeferencing world file yourself, and make sure it maps the image to the portion of the map that you want. 

World file naming conventions 

The world file associated with an image is named by following the conventions in the table below. For example, if you have an image that's stored in a file named myimage.bmp, then the world file associated with it must be named myimage.bmpw or myimage.bpw. 


If the file extension of the image is the world file extension must be 
bmp bmpw or bpw 
jpg; jpeg jpgw or jgw 
tif; tff; tiff tfw 
gis gsw 
lan lnw 
bil blw 
bip bpw 
bsq bqw 
sid sdw 
sun snw 
rs; ras rsw 
rlc rcw









个人工具
名字空间

变换
操作
导航
工具箱