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")