Files
insar-management-system-v2/third_party/PyINT/pyint/downloader_gmtchina.py
T

389 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*
#'''
#This code is used to download image from google and amap
#@date : 2020-3-13
#@author: Zheng Jie
#@E-mail: zhengjie9510@qq.com
#Modified by GMT-china.org
#'''
import sys
import io
import math
import multiprocessing
import time
import urllib.request as ur
from math import floor, pi, log, tan, atan, exp
from threading import Thread
import PIL.Image as pil
import cv2
import numpy as np
from osgeo import gdal, osr
# ------------------Interchange between WGS-84 and Web Mercator-------------------------
# WGS-84 to Web Mercator
def wgs_to_mercator(x, y):
y = 85.0511287798 if y > 85.0511287798 else y
y = -85.0511287798 if y < -85.0511287798 else y
x2 = x * 20037508.34 / 180
y2 = log(tan((90 + y) * pi / 360)) / (pi / 180)
y2 = y2 * 20037508.34 / 180
return x2, y2
# Web Mercator to WGS-84
def mercator_to_wgs(x, y):
x2 = x / 20037508.34 * 180
y2 = y / 20037508.34 * 180
y2 = 180 / pi * (2 * atan(exp(y2 * pi / 180)) - pi / 2)
return x2, y2
# --------------------------------------------------------------------------------------
# -----------------Interchange between GCJ-02 to WGS-84---------------------------
# All public geographic data in mainland China need to be encrypted with GCJ-02, introducing random bias
# This part of the code is used to remove the bias
def transformLat(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):
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
ret += (150.0 * math.sin(x / 12.0 * math.pi) + 300.0 * math.sin(x / 30.0 * math.pi)) * 2.0 / 3.0
return ret
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: Projection factor of satellite ellipsoidal coordinates projected onto a flat map coordinate system
ee = 0.00669342162296594323 # ee: Eccentricity of ellipsoid
dLat = transformLat(lon - 105.0, lat - 35.0)
dLon = transformLon(lon - 105.0, lat - 35.0)
radLat = lat / 180.0 * math.pi
magic = math.sin(radLat)
magic = 1 - ee * magic * magic
sqrtMagic = math.sqrt(magic)
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * math.pi)
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):
return True
if (lat < 0.8293 or lat > 55.8271):
return True
return False
def gcj_to_wgs(gcjLon, gcjLat):
if outOfChina(gcjLat, gcjLon):
return (gcjLon, gcjLat)
d = delta(gcjLat, gcjLon)
return (gcjLon - d["lon"], gcjLat - d["lat"])
def wgs_to_gcj(wgsLon, wgsLat):
if outOfChina(wgsLat, wgsLon):
return wgsLon, wgsLat
d = delta(wgsLat, wgsLon)
return wgsLon + d["lon"], wgsLat + d["lat"]
# --------------------------------------------------------------
# ---------------------------------------------------------
# Get tile coordinates in Google Maps based on latitude and longitude of WGS-84
def wgs_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)):
raise TypeError("j and w must be int or float!")
if not isinstance(z, int) or z < 0 or z > 22:
raise TypeError("z must be int and between 0 to 22.")
if j < 0:
j = 180 + j
else:
j += 180
j /= 360 # make j to (0,1)
w = 85.0511287798 if w > 85.0511287798 else w
w = -85.0511287798 if w < -85.0511287798 else w
w = log(tan((90 + w) * pi / 360)) / (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
num = 2 ** z
x = floor(j * num)
y = floor(w * num)
return x, y
def pixls_to_mercator(zb):
# Get the web Mercator projection coordinates of the four corners of the area according to the four corner coordinates of the tile
inx, iny = zb["LT"] # left top
inx2, iny2 = zb["RB"] # right bottom
length = 20037508.3427892
sum = 2 ** zb["z"]
LTx = inx / sum * length * 2 - length
LTy = -(iny / sum * length * 2) + length
RBx = (inx2 + 1) / sum * length * 2 - length
RBy = -((iny2 + 1) / sum * length * 2) + length
# LT=left top,RB=right buttom
# Returns the projected coordinates of the four corners
res = {'LT': (LTx, LTy), 'RB': (RBx, RBy),
'LB': (LTx, RBy), 'RT': (RBx, LTy)}
return res
def tile_to_pixls(zb):
# Tile coordinates are converted to pixel coordinates of the four corners
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
# -----------------------------------------------------------
# ---------------------------------------------------------
class Downloader(Thread):
# multiple threads downloader
def __init__(self, index, count, urls, datas):
# index represents the number of threads
# count represents the total number of threads
# urls represents the list of URLs nedd to be downloaded
# datas represents the list of data need to be returned.
super().__init__()
self.urls = urls
self.datas = datas
self.index = index
self.count = count
def download(self, url):
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.68'}
header = ur.Request(url, headers=HEADERS)
err = 0
while (err < 3):
try:
data = ur.urlopen(header).read()
except:
err += 1
else:
return data
raise Exception("Bad network link.")
def run(self):
for i, url in enumerate(self.urls):
if i % self.count != self.index:
continue
self.datas[i] = self.download(url)
# ---------------------------------------------------------
# ---------------------------------------------------------
def getExtent(x1, y1, x2, y2, z, source="amap"):
pos1x, pos1y = wgs_to_tile(x1, y1, z)
pos2x, pos2y = wgs_to_tile(x2, y2, z)
Xframe = pixls_to_mercator(
{"LT": (pos1x, pos1y), "RT": (pos2x, pos1y), "LB": (pos1x, pos2y), "RB": (pos2x, pos2y), "z": z})
for i in ["LT", "LB", "RT", "RB"]:
Xframe[i] = mercator_to_wgs(*Xframe[i])
if source == "google_sat":
pass
elif source == "amap" or source == "amap_sat" or source == "google":
for i in ["LT", "LB", "RT", "RB"]:
Xframe[i] = gcj_to_wgs(*Xframe[i])
else:
raise Exception("Invalid argument: source.")
return Xframe
def saveTiff(r, g, b, gt, filePath):
fname_out = filePath
driver = gdal.GetDriverByName('GTiff')
# Create a 3-band dataset
dset_output = driver.Create(fname_out, r.shape[1], r.shape[0], 3, gdal.GDT_Byte)
dset_output.SetGeoTransform(gt)
try:
proj = osr.SpatialReference()
proj.ImportFromEPSG(4326)
dset_output.SetSpatialRef(proj)
except:
print("Error: Coordinate system setting failed")
dset_output.GetRasterBand(1).WriteArray(r)
dset_output.GetRasterBand(2).WriteArray(g)
dset_output.GetRasterBand(3).WriteArray(b)
dset_output.FlushCache()
dset_output = None
print("Image Saved")
# ---------------------------------------------------------
# ---------------------------------------------------------
MAP_URLS = {
"google": "https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}",
"google_sat": "https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}",
"amap": "https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
"amap_sat": "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}"
}
def get_url(source, x, y, z, style): #
if source == 'amap':
url = MAP_URLS["amap"].format(x=x, y=y, z=z)
elif source == 'amap_sat':
url = MAP_URLS["amap_sat"].format(x=x, y=y, z=z)
elif source == 'google':
url = MAP_URLS["google"].format(x=x, y=y, z=z)
elif source == 'google_sat':
url = MAP_URLS["google_sat"].format(x=x, y=y, z=z)
else:
raise Exception("Unknown Map Source ! ")
return url
def get_urls(x1, y1, x2, y2, z, source, style):
pos1x, pos1y = wgs_to_tile(x1, y1, z)
pos2x, pos2y = wgs_to_tile(x2, y2, z)
lenx = pos2x - pos1x + 1
leny = pos2y - pos1y + 1
print("Total tiles number{x} X {y}".format(x=lenx, y=leny))
urls = [get_url(source, i, j, z, style) for j in range(pos1y, pos1y + leny) for i in range(pos1x, pos1x + lenx)]
return urls
# ---------------------------------------------------------
# ---------------------------------------------------------
def merge_tiles(datas, x1, y1, x2, y2, z):
pos1x, pos1y = wgs_to_tile(x1, y1, z)
pos2x, pos2y = wgs_to_tile(x2, y2, z)
lenx = pos2x - pos1x + 1
leny = pos2y - pos1y + 1
outpic = pil.new('RGBA', (lenx * 256, 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))
print('Tiles merge completed')
return outpic
def download_tiles(urls, multi=10):
url_len = len(urls)
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) for i in range(multi)]
for i in tasks:
i.start()
for i in tasks:
i.join()
return datas
# ---------------------------------------------------------
# ---------------------------------------------------------
def main(left, top, right, bottom, zoom, filePath, style='s', server="amap"):
"""
Download images based on spatial extent.
East longitude is positive and west longitude is negative.
North latitude is positive, south latitude is negative.
Parameters
----------
left, top : left-top coordinate, for example (100.361,38.866)
right, bottom : right-bottom coordinate
z : zoom
filePath : File path for storing results, TIFF format
style :
m for map;
s for satellite;
y for satellite with label;
t for terrain;
p for terrain with label;
h for label;
source : Google or amap
"""
# ---------------------------------------------------------
# Get the urls of all tiles in the extent
urls = get_urls(left, top, right, bottom, zoom, server, style)
# Group URLs based on the number of CPU cores to achieve roughly equal amounts of tasks
urls_group = [urls[i:i + math.ceil(len(urls) / multiprocessing.cpu_count())] for i in
range(0, len(urls), math.ceil(len(urls) / multiprocessing.cpu_count()))]
# Each set of URLs corresponds to a process for downloading tile maps
print('Tiles downloading......')
pool = multiprocessing.Pool(multiprocessing.cpu_count())
results = pool.map(download_tiles, urls_group)
pool.close()
pool.join()
result = [x for j in results for x in j]
print('Tiles download complete')
# Combine downloaded tile maps into one map
outpic = merge_tiles(result, left, top, right, bottom, zoom)
outpic = outpic.convert('RGB')
r, g, b = cv2.split(np.array(outpic))
# Get the spatial information of the four corners of the merged map and use it for outputting
extent = getExtent(left, top, right, bottom, zoom, server)
gt = (extent['LT'][0], (extent['RB'][0] - extent['LT'][0]) / r.shape[1], 0, extent['LT'][1], 0,
(extent['RB'][1] - extent['LT'][1]) / r.shape[0])
saveTiff(r, g, b, gt, filePath)
# ---------------------------------------------------------
if __name__ == '__main__':
start_time = time.time()
#main(118.055917, 24.559724, 118.244753, 24.399450, 16, r'google_sat.tif', server="google_sat")
main(float(sys.argv[1]), float(sys.argv[4]), float(sys.argv[2]), float(sys.argv[3]), int(sys.argv[5]), sys.argv[6], server=sys.argv[7])
end_time = time.time()
print('lasted a total of {:.2f} seconds'.format(end_time - start_time))