diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aab52d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.png \ No newline at end of file diff --git a/.idea/getmap-master.iml b/.idea/getmap-master.iml new file mode 100644 index 0000000..e98082a --- /dev/null +++ b/.idea/getmap-master.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3999087 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1924aa0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 35b42d2..34bdd56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# pygetmap +# getmap 请使用python 3 @@ -17,3 +17,27 @@ 2.增加GCJ的纠偏功能 3.增加链接文件的输出,可用于arcgis的地理配准。输出文件可选择原样输出,或gcj02转wgs84,以及wgs84转gcj02。 + + +## 2018.04.12更新 + +增加了Golang的版本。 +增加了Golang编译好的可执行版本。 +``` +>getmap -v +Usage of getmap: + -f string + 输出文件名称(以.jpg结尾) (default "OUT[0412_211623].jpg") + -m string + s - 卫星图 m - 路网图 (default "s") + -n int + 下载线程数 (default 10) + -p1 string + 第一个对角点的经纬度,如 104.08028,30.67101 逗号前后不要加空格 + -p2 string + 第二个对角点的经纬度,如 104.08028,30.67101 逗号前后不要加空格 + -s string + 地图源(目前仅支持 google/amap/tencent) (default "google") + -z int + 缩放级别,级别越大图幅越清晰。请取[1,19] (default 2) +``` diff --git a/getmap.exe b/getmap.exe new file mode 100644 index 0000000..91a9936 Binary files /dev/null and b/getmap.exe differ diff --git a/getmap.go b/getmap.go new file mode 100644 index 0000000..21d634a --- /dev/null +++ b/getmap.go @@ -0,0 +1,288 @@ +//author:yuansu +//mail: idgensou@gmail.com +package main + +import ( + "bytes" + "flag" + "fmt" + "image" + "image/draw" + "image/jpeg" + "image/png" + "io/ioutil" + "log" + "math" + "net/http" + "os" + "time" +) + +var ( + urltemplate = map[string]string{"google": "http://mt2.google.cn/vt/lyrs=%s&hl=zh-CN&gl=CN&src=app&x=%d&y=%d&z=%d", + "amap": "http://wprd02.is.autonavi.com/appmaptile?style=%d&x=%d&y=%d&z=%d", + "tencent_s": "http://p3.map.gtimg.com/sateTiles/%d/%d/%d/%d_%d.jpg", + "tencent_m": "http://rt0.map.gtimg.com/tile?z=%d&x=%d&y=%d&styleid=3"} + //P2 mean pow(2,i) i->[0,19] + P2 = [20]int{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288} +) + +const ( + //AGENT 常用的浏览器user-agent字段 + AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36" +) + +type urlplace struct { + url string + x int + y int +} +type dataplace struct { + data []byte + x int + y int +} +type postion struct { + x, y int +} + +type params struct { + filename, source, mode string + x0, y0, x1, y1 float64 + z, n int +} + +// 根据瓦片坐标和地图源,得到瓦片图片的请求地址 +func formaturl(source string, x, y, z int, style string) (furl string) { + /* + Get the picture's url for download. + style: + m for map + s for satellite + source: + google or amap or tencent + x y: + google-style tile coordinate system + z: + zoom + */ + if source == "google" { + furl = fmt.Sprintf(urltemplate["google"], style, x, y, z) + } else if source == "amap" { + // for amap 6 is satellite and 7 is map. + var s int + if style == "s" { + s = 6 + } else { + s = 7 + } + furl = fmt.Sprintf(urltemplate["amap"], s, x, y, z) + } else if source == "tencent" { + y = P2[z] - 1 - y + if style == "s" { + furl = fmt.Sprintf(urltemplate["tencent_s"], z, x/16, y/16, x, y) + } else { + furl = fmt.Sprintf(urltemplate["tencent_s"], z, x, y) + } + + } else { + log.Fatal("Unknown Map Source!") + } + + return +} + +// 根据WGS-84 的经纬度获取谷歌地图中的瓦片坐标 +func wgs84ToTile(j, w float64, z int) (x, y int) { + /* + Get google-style tile cooridinate from geographical coordinate + j : Longittude + w : Latitude + z : zoom + */ + + // make j to (0,1) + j += 180 + j /= 360 + + if w > 85.0511287798 { + w = 85.0511287798 + } + if w < -85.0511287798 { + w = -85.0511287798 + } + w = math.Log(math.Tan((90+w)*math.Pi/360)) / (math.Pi / 180) + w /= 180 // make w to (-1,1) + w = 1 - (w+1)/2 // make w to (0,1) and left top is 0-point + + x = int(j * float64(P2[z])) + y = int(w * float64(P2[z])) + + return +} + +func ccloser(n int, cclose chan int, datac chan dataplace) { + for i := 0; i < n; i++ { + <-cclose + } + close(datac) + +} + +// 下载瓦片地图 +func downloader(client *http.Client, urlc chan urlplace, datac chan dataplace, cclose chan int, monitorc chan int) { + for i := range urlc { + request, _ := http.NewRequest("GET", i.url, nil) + request.Header.Set("User-Agent", AGENT) + res, err := client.Do(request) + if err != nil { + log.Fatal("download ", i, " Fail !!") + } + b, _ := ioutil.ReadAll(res.Body) + monitorc <- 0 + datac <- dataplace{b, i.x, i.y} + } + cclose <- 0 //this thread has downloaded +} + +// 把瓦片地图合并到一张大图里面 +func merger(big *image.NRGBA, datac chan dataplace, outimgc chan *image.NRGBA) { + var img image.Image + var err error + for it := range datac { + // tile is PNG + if string(it.data[1:4]) == "PNG" { + img, err = png.Decode(bytes.NewReader(it.data)) + if err != nil { + fmt.Println("瓦片PNG解析失败!") + log.Fatal(err.Error()) + } + } else { //tile is JPG + img, err = jpeg.Decode(bytes.NewReader(it.data)) + if err != nil { + fmt.Println("瓦片JPG解析失败!") + log.Fatal(err.Error()) + } + } + draw.Draw(big, image.Rect(it.x*256, it.y*256, it.x*256+256, it.y*256+256), img, image.Point{0, 0}, draw.Src) + + } + outimgc <- big +} +func monitor(sum int, count chan int) { + for i := 0; i < sum; i++ { + fmt.Printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b正在下载及拼合图像... [%d/%d]", i+1, sum) + <-count + } + fmt.Println("\n下载完成。还在拼合图像...") +} + +// 建立一个大图片 +func makeBigImg(width, height int) *image.NRGBA { + return image.NewNRGBA(image.Rect(0, 0, 256*width, 256*height)) +} + +//根据矩形对角的两个瓦片坐标,返回出矩形内所有的瓦片坐标,以及瓦片的横纵数量 +func makeRange(x0, y0, x1, y1 int) (r []postion, width, height int) { + if x0 > x1 { + x0, x1 = x1, x0 + } + if y0 > y1 { + y0, y1 = y1, y0 + } + height = y1 - y0 + 1 + width = x1 - x0 + 1 + for yt := y0; yt <= y1; yt++ { + for xt := x0; xt <= x1; xt++ { + r = append(r, postion{xt, yt}) + } + } + return +} + +//经度 Longitude +//纬度 Latitude + +//Getmap 根据矩形对角的经纬度,下载矩形范围内所有地图,并拼合,写入f中。 +func Getmap(f *os.File, source string, maptype string, lng0, lat0, lng1, lat1 float64, z int, multi int) { + x0, y0 := wgs84ToTile(lng0, lat0, z) + x1, y1 := wgs84ToTile(lng1, lat1, z) + tiles, w, h := makeRange(x0, y0, x1, y1) + + big := makeBigImg(w, h) + client := &http.Client{} + + urlc := make(chan urlplace, 20) + datac := make(chan dataplace, 20) + imgc := make(chan *image.NRGBA) + closec := make(chan int) + monitorc := make(chan int) + + go monitor(w*h, monitorc) + go ccloser(multi, closec, datac) + go merger(big, datac, imgc) + + //多线程下载 + for i := 0; i < multi; i++ { + go downloader(client, urlc, datac, closec, monitorc) + } + + for i, v := range tiles { + urlc <- urlplace{formaturl(source, v.x, v.y, z, maptype), i % w, i / w} + } + close(urlc) + jpeg.Encode(f, <-imgc, nil) + +} + +func parsecl() params { + var filename, source, mode, p1, p2 string + var zoom, n int + defaultname := time.Now().Format("OUT[0102_150405].jpg") + flag.StringVar(&filename, "f", defaultname, "输出文件名称(以.jpg结尾)") + flag.StringVar(&source, "s", "google", "地图源(目前仅支持 google/amap/tencent)") + flag.StringVar(&mode, "m", "s", "s - 卫星图 m - 路网图") + flag.StringVar(&p1, "p1", "", "第一个对角点的经纬度,如 104.08028,30.67101 逗号前后不要加空格") + flag.StringVar(&p2, "p2", "", "第二个对角点的经纬度,如 104.08028,30.67101 逗号前后不要加空格") + flag.IntVar(&zoom, "z", 2, "缩放级别,级别越大图幅越清晰。请取[1,19]") + flag.IntVar(&n, "n", 10, "下载线程数") + flag.Parse() + var x0, y0, x1, y1 float64 + var err error + _, err = fmt.Sscanf(p1, "%f,%f", &x0, &y0) + if err != nil { + log.Fatal("参数p1输入错误") + } + _, err = fmt.Sscanf(p2, "%f,%f", &x1, &y1) + if err != nil { + log.Fatal("参数p2输入错误") + } + if source != "google" && source != "amap" && source != "tencent" { + log.Fatal("地图源设置错误!仅支持高德(amap)、谷歌(google)和腾讯(tencent)。") + } + if mode != "m" && mode != "s" { + log.Fatal("m地图模式设置错误!仅支持卫星图(s)和路网图(m)。") + } + if n < 1 || n > 100 { + n = 10 + } + if zoom < 1 || zoom > 20 { + log.Fatal("缩放级别设置错误!请设置在[1,19]。") + } + return params{filename, source, mode, x0, y0, x1, y1, zoom, n} + +} + +func main() { + log.SetFlags(log.Ltime) + p := parsecl() + + f, err := os.Create(p.filename) + if err != nil { + log.Fatal("创建文件失败!程序退出。") + } + Getmap(f, p.source, p.mode, p.x0, p.y0, p.x1, p.y1, p.z, p.n) + f.Close() + fmt.Println("拼合完成!") + +} diff --git a/getmap.py b/getmap.py index c6e3d5d..cf70dd4 100644 --- a/getmap.py +++ b/getmap.py @@ -1,9 +1,11 @@ -''' +# coding=utf-8 + +""" pygetmap: Download web map by cooridinates -''' +""" # Longitude 经度 # Latitude 纬度 @@ -11,31 +13,36 @@ # Mecator Latitue = [-85.05112877980659,85.05112877980659] import math +import os + +import requests +import sys from math import floor, pi, log, tan, atan, exp from threading import Thread, Lock -import urllib.request as ur -import PIL.Image as pil +from PIL import Image import io +import traceback MAP_URLS = { "google": "http://mt2.google.cn/vt/lyrs={style}&hl=zh-CN&gl=CN&src=app&x={x}&y={y}&z={z}", "amap": "http://wprd02.is.autonavi.com/appmaptile?style={style}&x={x}&y={y}&z={z}", "tencent_s": "http://p3.map.gtimg.com/sateTiles/{z}/{fx}/{fy}/{x}_{y}.jpg", - "tencent_m": "http://rt0.map.gtimg.com/tile?z={z}&x={x}&y={y}&styleid=3" } + "tencent_m": "http://rt0.map.gtimg.com/tile?z={z}&x={x}&y={y}&styleid=3"} -COUNT=0 -mutex=Lock() +COUNT = 0 +mutex = Lock() -#-----------------GCJ02到WGS84的纠偏与互转--------------------------- -def transformLat(x, y): +# -----------------GCJ02到WGS84的纠偏与互转--------------------------- +def transform_lat(x, y): ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * math.sqrt(abs(x)) ret += (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi)) * 2.0 / 3.0 ret += (20.0 * math.sin(y * math.pi) + 40.0 * math.sin(y / 3.0 * math.pi)) * 2.0 / 3.0 ret += (160.0 * math.sin(y / 12.0 * math.pi) + 320 * math.sin(y * math.pi / 30.0)) * 2.0 / 3.0 return ret -def transformLon(x, y): + +def transform_lon(x, y): ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * math.sqrt(abs(x)) ret += (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi)) * 2.0 / 3.0 ret += (20.0 * math.sin(x * math.pi) + 40.0 * math.sin(x / 3.0 * math.pi)) * 2.0 / 3.0 @@ -44,17 +51,17 @@ def transformLon(x, y): def delta(lat, lon): - ''' + """ Krasovsky 1940 // // a = 6378245.0, 1/f = 298.3 // b = a * (1 - f) // ee = (a^2 - b^2) / a^2; - ''' - a = 6378245.0 # a: 卫星椭球坐标投影到平面地图坐标系的投影因子。 - ee = 0.00669342162296594323 # ee: 椭球的偏心率。 - dLat = transformLat(lon - 105.0, lat - 35.0) - dLon = transformLon(lon - 105.0, lat - 35.0) + """ + a = 6378245.0 # a: 卫星椭球坐标投影到平面地图坐标系的投影因子。 + ee = 0.00669342162296594323 # ee: 椭球的偏心率。 + dLat = transform_lat(lon - 105.0, lat - 35.0) + dLon = transform_lon(lon - 105.0, lat - 35.0) radLat = lat / 180.0 * math.pi magic = math.sin(radLat) magic = 1 - ee * magic * magic @@ -63,28 +70,32 @@ def delta(lat, lon): dLon = (dLon * 180.0) / (a / sqrtMagic * math.cos(radLat) * math.pi) return {'lat': dLat, 'lon': dLon} -def outOfChina(lat, lon): - if (lon < 72.004 or lon > 137.8347): + +def out_of_china(lat, lon): + if lon < 72.004 or lon > 137.8347: return True - if (lat < 0.8293 or lat > 55.8271): + if lat < 0.8293 or lat > 55.8271: return True return False -def gcj_to_wgs(gcjLon,gcjLat): - if outOfChina(gcjLat, gcjLon): + +def gcj_to_wgs(gcjLon, gcjLat): + if out_of_china(gcjLat, gcjLon): return (gcjLon, gcjLat) d = delta(gcjLat, gcjLon) - return (gcjLon - d["lon"],gcjLat - d["lat"]) + return gcjLon - d["lon"], gcjLat - d["lat"] -def wgs_to_gcj(wgsLon,wgsLat): - if outOfChina(wgsLat, wgsLon): + +def wgs_to_gcj(wgsLon, wgsLat): + if out_of_china(wgsLat, wgsLon): return wgsLon, wgsLat - d = delta(wgsLat, wgsLon); + d = delta(wgsLat, wgsLon) return wgsLon + d["lon"], wgsLat + d["lat"] -#-------------------------------------------------------------- -#------------------wgs84与web墨卡托互转------------------------- +# -------------------------------------------------------------- + +# ------------------wgs84与web墨卡托互转------------------------- # WGS-84经纬度转Web墨卡托 def wgs_to_macator(x, y): @@ -96,6 +107,7 @@ def wgs_to_macator(x, y): y2 = y2 * 20037508.34 / 180 return x2, y2 + # Web墨卡托转经纬度 def mecator_to_wgs(x, y): x2 = x / 20037508.34 * 180 @@ -103,24 +115,27 @@ def mecator_to_wgs(x, y): y2 = 180 / pi * (2 * atan(exp(y2 * pi / 180)) - pi / 2) return x2, y2 -#------------------------------------------------------------- -#--------------------------------------------------------- +# ------------------------------------------------------------- + +# --------------------------------------------------------- ''' 东经为正,西经为负。北纬为正,南纬为负 j经度 w纬度 z缩放比例[0-22] ,对于卫星图并不能取到最大,测试值是20最大,再大会返回404. 山区卫星图可取的z更小,不同地图来源设置不同。 ''' + + # 根据WGS-84 的经纬度获取谷歌地图中的瓦片坐标 def wgs84_to_tile(j, w, z): - ''' + """ Get google-style tile cooridinate from geographical coordinate j : Longittude w : Latitude z : zoom - ''' + """ isnum = lambda x: isinstance(x, int) or isinstance(x, float) - if not(isnum(j) and isnum(w)): + if not (isnum(j) and isnum(w)): raise TypeError("j and w must be int or float!") if not isinstance(z, int) or z < 0 or z > 22: @@ -138,19 +153,18 @@ def wgs84_to_tile(j, w, z): w /= 180 # make w to (-1,1) w = 1 - (w + 1) / 2 # make w to (0,1) and left top is 0-point - num = 2**z + num = 2 ** z x = floor(j * num) y = floor(w * num) return x, y - def tileframe_to_mecatorframe(zb): # 根据瓦片四角坐标,获得该区域四个角的web墨卡托投影坐标 - inx, iny =zb["LT"] #left top - inx2,iny2=zb["RB"] #right bottom + inx, iny = zb["LT"] # left top + inx2, iny2 = zb["RB"] # right bottom length = 20037508.3427892 - sum = 2**zb["z"] + sum = 2 ** zb["z"] LTx = inx / sum * length * 2 - length LTy = -(iny / sum * length * 2) + length @@ -163,56 +177,84 @@ def tileframe_to_mecatorframe(zb): 'LB': (LTx, RBy), 'RT': (RBx, LTy)} return res + def tileframe_to_pixframe(zb): # 瓦片坐标转化为最终图片的四个角像素的坐标 - out={} - width=(zb["RT"][0]-zb["LT"][0]+1)*256 - height=(zb["LB"][1]-zb["LT"][1]+1)*256 - out["LT"]=(0,0) - out["RT"]=(width,0) - out["LB"]=(0,-height) - out["RB"]=(width,-height) + out = {} + width = (zb["RT"][0] - zb["LT"][0] + 1) * 256 + height = (zb["LB"][1] - zb["LT"][1] + 1) * 256 + out["LT"] = (0, 0) + out["RT"] = (width, 0) + out["LB"] = (0, -height) + out["RB"] = (width, -height) return out -#----------------------------------------------------------- + +def mkdir(path): + path = path.strip() + path = path.strip('/') + path = '/'.join(path.replace('\\', '/').split('/')[:-1]) + if not os.path.exists(path): + os.makedirs(path) + + +HEADERS = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/29.0.1547.76 Safari/537.36'} class Downloader(Thread): # multiple threads downloader - def __init__(self,index,count,urls,datas,update): + def __init__(self, index, count, urls_filenames, datas): # index 表示第几个线程,count 表示线程的总数,urls 代表需要下载url列表,datas代表要返回的数据列表。 # update 表示每下载一个成功就进行的回调函数。 super().__init__() - self.urls=urls - self.datas=datas - self.index=index - self.count=count - self.update=update - - def download(self,url): - HEADERS = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36'} - header = ur.Request(url,headers=HEADERS) - err=0 - while(err<3): + self.urls_filenames = urls_filenames + self.datas = datas + self.index = index + self.count = count + + @staticmethod + def download(url, filename): + if filename and os.path.exists(filename): + with mutex: + print("From disk") + with open(filename, "rb") as f: + return f.read() + mkdir(filename) + + with mutex: + print("downloading", url) + err = 0 + while err < 3: try: - data = ur.urlopen(header).read() - except: - err+=1 + req = requests.get(url, headers=HEADERS) + if "html" in req.text[:20]: + raise Exception("Server/Network error") + except Exception as e: + # traceback.print_exc() + print(e, file=sys.stderr) + err += 1 else: - return data + if filename: + with open(filename, "wb") as f: + f.write(req.content) + with mutex: + print("downloaded", url) + return req.content raise Exception("Bad network link.") def run(self): - for i,url in enumerate(self.urls): - if i%self.count != self.index: + for i in range(len(self.urls_filenames)): + url = self.urls_filenames[i][0] + filename = self.urls_filenames[i][1] + if i % self.count != self.index: continue - self.datas[i]=self.download(url) - if mutex.acquire(): - self.update() - mutex.release() + self.datas[i] = self.download(url, filename) + def geturl(source, x, y, z, style): - ''' + """ Get the picture's url for download. style: m for map @@ -222,8 +264,8 @@ def geturl(source, x, y, z, style): x y: google-style tile coordinate system z: - zoom - ''' + zoom + """ if source == 'google': furl = MAP_URLS["google"].format(x=x, y=y, z=z, style=style) elif source == 'amap': @@ -231,7 +273,7 @@ def geturl(source, x, y, z, style): style = 6 if style == 's' else 7 furl = MAP_URLS["amap"].format(x=x, y=y, z=z, style=style) elif source == 'tencent': - y = 2**z - 1 - y + y = 2 ** z - 1 - y if style == 's': furl = MAP_URLS["tencent_s"].format( x=x, y=y, z=z, fx=floor(x / 16), fy=floor(y / 16)) @@ -243,23 +285,12 @@ def geturl(source, x, y, z, style): return furl - - -def downpics(urls,multi=10): - - def makeupdate(s): - def up(): - global COUNT - COUNT+=1 - print("\b"*45,end='') - print("DownLoading ... [{0}/{1}]".format(COUNT,s),end='') - return up - - url_len=len(urls) - datas=[None] * url_len - if multi <1 or multi >20 or not isinstance(multi,int): +def downpics(urls_filenames, multi=10): + url_len = len(urls_filenames) + datas = [None] * url_len + if multi < 1 or multi > 20 or not isinstance(multi, int): raise Exception("multi of Downloader shuold be int and between 1 to 20.") - tasks=[Downloader(i,multi,urls,datas,makeupdate(url_len)) for i in range(multi)] + tasks = [Downloader(i, multi, urls_filenames, datas) for i in range(multi)] for i in tasks: i.start() for i in tasks: @@ -268,86 +299,97 @@ def up(): return datas +def num_hash(*args): + h = 66666 + for e in args: + h = h * 3666 + e + return int(h) % 31567 + + def getpic(x1, y1, x2, y2, z, source='google', outfile="MAP_OUT.png", style='s'): - ''' + """ 依次输入左上角的经度、纬度,右下角的经度、纬度,缩放级别,地图源,输出文件,影像类型(默认为卫星图) 获取区域内的瓦片并自动拼合图像。返回四个角的瓦片坐标 - ''' + """ pos1x, pos1y = wgs84_to_tile(x1, y1, z) pos2x, pos2y = wgs84_to_tile(x2, y2, z) - lenx = pos2x - pos1x + 1 - leny = pos2y - pos1y + 1 + lenx = math.ceil(pos2x - pos1x) + leny = math.ceil(pos2y - pos1y) print("Total number:{x} X {y}".format(x=lenx, y=leny)) - - urls = [geturl(source, i, j, z, style) for j in range(pos1y, pos1y + leny) for i in range(pos1x, pos1x + lenx)] - - datas = downpics(urls) - - print("\nDownload Finished! Pics Mergeing......") - outpic = pil.new('RGBA', (lenx * 256, leny * 256)) + urls_filenames = [] + for j in range(int(pos1y), int(pos1y + leny)): + for i in range(int(pos1x), int(pos1x + lenx)): + urls_filenames.append((geturl(source, i, j, z, style), + "{source}_{hash}_{z}/{i}_{j}_{name}" + .format(source=source, hash=num_hash(z), + i=i, j=j, z=z, name=outfile))) + + datas = downpics(urls_filenames) + + print("\nDownload Finished! Pics Merging......") + outpic = Image.new('RGBA', (int(lenx * 256), int(leny * 256))) for i, data in enumerate(datas): - picio = io.BytesIO(data) - small_pic = pil.open(picio) - - y, x = i // lenx, i % lenx - outpic.paste(small_pic, (x * 256, y * 256)) + try: + small_pic = Image.open(io.BytesIO(data)) + y, x = i // int(lenx), i % int(lenx) + outpic.paste(small_pic, (x * 256, y * 256)) + except: + print(i, file=sys.stderr) + pass print('Pics Merged! Exporting......') - outpic.save(outfile) + outpic.save("{source}_{hash}_{z}_{name}" + .format(source=source, hash=num_hash(x1, y1, x2, y2), + z=z, name=outfile)) print('Exported to file!') - return {"LT":(pos1x,pos1y),"RT":(pos2x,pos1y),"LB":(pos1x,pos2y),"RB":(pos2x,pos2y),"z":z} + return {"LT": (pos1x, pos1y), "RT": (pos2x, pos1y), "LB": (pos1x, pos2y), "RB": (pos2x, pos2y), "z": z} - -def screen_out(zb,name): +def screen_out(zb, name): if not zb: print("N/A") return - print("坐标形式:",name) - print("左上:({0:.7f},{1:.7f})".format(*zb['LT'])) - print("右上:({0:.7f},{1:.7f})".format(*zb['RT'])) - print("左下:({0:.7f},{1:.7f})".format(*zb['LB'])) - print("右下:({0:.7f},{1:.7f})".format(*zb['RB'])) + print("坐标形式:", name) + print("左上:({0:.5f},{1:.5f})".format(*zb['LT'])) + print("右上:({0:.5f},{1:.5f})".format(*zb['RT'])) + print("左下:({0:.5f},{1:.5f})".format(*zb['LB'])) + print("右下:({0:.5f},{1:.5f})".format(*zb['RB'])) -def file_out(zb,file,target="keep",output="file"): - ''' +def file_out(zb, file, target="keep", output="file"): + """ zh_in : tile coordinate file : a text file for ArcGis target : keep = tile to Geographic coordinate gcj = tile to Geographic coordinate,then wgs84 to gcj wgs = tile to Geographic coordinate,then gcj02 to wgs84 - ''' - pixframe=tileframe_to_pixframe(zb) - Xframe=tileframe_to_mecatorframe(zb) - for i in ["LT","LB","RT","RB"]: - Xframe[i]=mecator_to_wgs(*Xframe[i]) - if target =="keep": + """ + pixframe = tileframe_to_pixframe(zb) + Xframe = tileframe_to_mecatorframe(zb) + for i in ["LT", "LB", "RT", "RB"]: + Xframe[i] = mecator_to_wgs(*Xframe[i]) + if target == "keep": pass; elif target == "gcj": - for i in ["LT","LB","RT","RB"]: - Xframe[i]=wgs_to_gcj(*Xframe[i]) + for i in ["LT", "LB", "RT", "RB"]: + Xframe[i] = wgs_to_gcj(*Xframe[i]) elif target == "wgs": - for i in ["LT","LB","RT","RB"]: - Xframe[i]=gcj_to_wgs(*Xframe[i]) + for i in ["LT", "LB", "RT", "RB"]: + Xframe[i] = gcj_to_wgs(*Xframe[i]) else: raise Exception("Invalid argument: target.") - - if output=="file": - f=open(file,"w") - for i in ["LT","LB","RT","RB"]: - f.write("{0[0]}, {0[1]}, {1[0]}, {1[1]}\n".format(pixframe[i],Xframe[i])) + + if output == "file": + f = open(file, "w") + for i in ["LT", "LB", "RT", "RB"]: + f.write("{0[0]:.5f}, {0[1].5f}, {1[0].5f}, {1[1].5f}\n".format(pixframe[i], Xframe[i])) f.close() - print("Exported link file to ",file) + print("Exported link file to ", file) else: - screen_out(Xframe,target) - - - + screen_out(Xframe, target) if __name__ == '__main__': - - x = getpic(109.27305,32.869698, 109.42291,32.719769, - 17, source='amap', style='m', outfile="xunyang_17m.jpg") - file_out(x,"zb17.txt","wgs") + x = getpic(-180, 90, 180, -90, + 6, source='google', style='s', outfile="earth.png") + # file_out(x, "zb17.txt", "wgs")