Skip to content

Migrated to map-tiler #976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/proj/reproj.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from .srs import SRS
from .utm import UTM, UTM_EPSG_CODES
from .ellps import GRS80
from .srv import EPSGIO
from .srv import MapTilerCoordinates

from ..errors import ReprojError
from ..utils import BBOX
Expand Down
118 changes: 74 additions & 44 deletions core/proj/srv.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@
REPROJ_TIMEOUT = 60

######################################
# EPSG.io
# https://github.com/klokantech/epsg.io
# MapTiler Coordinates API (formerly EPSG.io)
# Migration guide: https://docs.maptiler.com/cloud/api/coordinates/


class EPSGIO():
class MapTilerCoordinates():

@staticmethod
def ping():
url = "http://epsg.io"
def ping(api_key=None):
"""Test connection to MapTiler API server"""
if api_key is None:
api_key = settings.maptiler_api_key

url = "https://api.maptiler.com/coordinates/search/epsg.json?key=" + api_key
try:
rq = Request(url, headers={'User-Agent': USER_AGENT})
urlopen(rq, timeout=DEFAULT_TIMEOUT)
Expand All @@ -54,17 +57,20 @@ def ping():
except:
raise


@staticmethod
def reprojPt(epsg1, epsg2, x1, y1):

url = "http://epsg.io/trans?x={X}&y={Y}&z={Z}&s_srs={CRS1}&t_srs={CRS2}"
def reprojPt(epsg1, epsg2, x1, y1, api_key=None):
"""Reproject a single point using MapTiler Coordinates API"""
if api_key is None:
api_key = settings.maptiler_api_key

# New endpoint format with API key
url = "https://api.maptiler.com/coordinates/transform/{X},{Y}.json?s_srs={CRS1}&t_srs={CRS2}&key={KEY}"

url = url.replace("{X}", str(x1))
url = url.replace("{Y}", str(y1))
url = url.replace("{Z}", '0')
url = url.replace("{CRS1}", str(epsg1))
url = url.replace("{CRS2}", str(epsg2))
url = url.replace("{KEY}", api_key)

log.debug(url)

Expand All @@ -77,38 +83,37 @@ def reprojPt(epsg1, epsg2, x1, y1):

obj = json.loads(response)

return (float(obj['x']), float(obj['y']))
# The MapTiler response format is different from the old EPSG.io format
# MapTiler returns coordinates as an array in the response body
return (float(obj[0]), float(obj[1]))

@staticmethod
def reprojPts(epsg1, epsg2, points):

def reprojPts(epsg1, epsg2, points, api_key=None):
"""Reproject multiple points using MapTiler Coordinates API"""
if api_key is None:
api_key = settings.maptiler_api_key

if len(points) == 1:
x, y = points[0]
return [EPSGIO.reprojPt(epsg1, epsg2, x, y)]
return [MapTilerCoordinates.reprojPt(epsg1, epsg2, x, y, api_key=api_key)]

urlTemplate = "http://epsg.io/trans?data={POINTS}&s_srs={CRS1}&t_srs={CRS2}"
# New endpoint with batch transformation (up to 50 points)
urlTemplate = "https://api.maptiler.com/coordinates/transform/{POINTS}.json?s_srs={CRS1}&t_srs={CRS2}&key={KEY}"

urlTemplate = urlTemplate.replace("{CRS1}", str(epsg1))
urlTemplate = urlTemplate.replace("{CRS2}", str(epsg2))

#data = ';'.join([','.join(map(str, p)) for p in points])
urlTemplate = urlTemplate.replace("{KEY}", api_key)

precision = 4
data = [','.join( [str(round(v, precision)) for v in p] ) for p in points ]
part, parts = [], []
for i,p in enumerate(data):
l = sum([len(p) for p in part]) + len(';'*len(part))
if l + len(p) < 4000: #limit is 4094
part.append(p)
else:
parts.append(part)
part = [p]
if i == len(data)-1:
parts.append(part)
parts = [';'.join(part) for part in parts]

data = [','.join([str(round(v, precision)) for v in p]) for p in points]

# MapTiler API supports up to 50 points per request in batch mode
batch_size = 50
batches = [data[i:i + batch_size] for i in range(0, len(data), batch_size)]

result = []
for part in parts:
for batch in batches:
part = ';'.join(batch)
url = urlTemplate.replace("{POINTS}", part)
log.debug(url)

Expand All @@ -120,32 +125,58 @@ def reprojPts(epsg1, epsg2, points):
raise

obj = json.loads(response)
result.extend( [(float(p['x']), float(p['y'])) for p in obj] )

# MapTiler API returns an array of coordinate pairs
result.extend([(float(p[0]), float(p[1])) for p in obj])

return result

@staticmethod
def search(query):
def search(query, api_key=None):
"""Search coordinate systems using MapTiler Coordinates API"""
if api_key is None:
api_key = settings.maptiler_api_key

query = str(query).replace(' ', '+')
url = "http://epsg.io/?q={QUERY}&format=json"
# New endpoint with API key
url = "https://api.maptiler.com/coordinates/search/{QUERY}.json?key={KEY}"
url = url.replace("{QUERY}", query)
url = url.replace("{KEY}", api_key)

log.debug('Search crs : {}'.format(url))
rq = Request(url, headers={'User-Agent': USER_AGENT})
response = urlopen(rq, timeout=DEFAULT_TIMEOUT).read().decode('utf8')
obj = json.loads(response)
log.debug('Search results : {}'.format([ (r['code'], r['name']) for r in obj['results'] ]))

log.debug('Search results : {}'.format([(r['id']['code'], r['name']) for r in obj['results']]))
return obj['results']

@staticmethod
def getEsriWkt(epsg):
url = "http://epsg.io/{CODE}.esriwkt"
def getEsriWkt(epsg, api_key=None):
"""Get ESRI WKT for a specific EPSG code using MapTiler Coordinates API"""
if api_key is None:
api_key = settings.maptiler_api_key

# New endpoint with API key
url = "https://api.maptiler.com/coordinates/search/{CODE}.json?exports=true&key={KEY}"
url = url.replace("{CODE}", str(epsg))
url = url.replace("{KEY}", api_key)

log.debug(url)
rq = Request(url, headers={'User-Agent': USER_AGENT})
wkt = urlopen(rq, timeout=DEFAULT_TIMEOUT).read().decode('utf8')
return wkt
response = urlopen(rq, timeout=DEFAULT_TIMEOUT).read().decode('utf8')
obj = json.loads(response)

if obj['results'] and len(obj['results']) > 0 and 'exports' in obj['results'][0]:
return obj['results'][0]['exports']['esriwkt']
else:
log.error('Could not find ESRI WKT for EPSG:{}'.format(epsg))
return None


# For backward compatibility, you can keep the EPSGIO class as an alias to MapTilerCoordinates
class EPSGIO(MapTilerCoordinates):
pass


######################################
Expand Down Expand Up @@ -173,11 +204,10 @@ def reprojPt(epsg1, epsg2, x1, y1):


######################################
#http://spatialreference.org/ref/epsg/2154/esriwkt/

#class SpatialRefOrg():
# http://spatialreference.org/ref/epsg/2154/esriwkt/

# class SpatialRefOrg():


######################################
#http://prj2epsg.org/search
# http://prj2epsg.org/search
31 changes: 23 additions & 8 deletions prefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import addon_utils

from . import bl_info
from .core.proj.reproj import EPSGIO
from .core.proj.reproj import MapTilerCoordinates
from .core.proj.srs import SRS
from .core.checkdeps import HAS_GDAL, HAS_PYPROJ, HAS_PIL, HAS_IMGIO
from .core import settings
Expand Down Expand Up @@ -241,7 +241,10 @@ def listDemServer(self, context):
name = "",
description="you need to register and request a key from opentopography website"
)

maptiler_api_key: StringProperty(
name = "",
description = "API key for MapTiler Coordinates API (required for EPSG.io migration)"
)

################
#IO options
Expand Down Expand Up @@ -333,6 +336,11 @@ def draw(self, context):
row = box.row()
row.label(text="Opentopography Api Key")
box.row().prop(self, "opentopography_api_key")

row = box.row()
row.label(text="MapTiler API Key")
box.row().prop(self, "maptiler_api_key")

#System
box = layout.box()
box.label(text='System')
Expand Down Expand Up @@ -387,21 +395,28 @@ def check(self, context):
return True

def search(self, context):
if not EPSGIO.ping():
self.report({'ERROR'}, "Cannot request epsg.io website")
prefs = context.preferences.addons[PKG].preferences
api_key = prefs.maptiler_api_key

if not api_key:
self.report({'ERROR'}, "MapTiler API key is required. Please set it in the preferences.")
return

if not MapTilerCoordinates.ping(api_key=api_key):
self.report({'ERROR'}, "Cannot connect to MapTiler API")
else:
results = EPSGIO.search(self.query)
results = MapTilerCoordinates.search(self.query, api_key=api_key)
self.results = json.dumps(results)
if results:
self.crs = 'EPSG:' + results[0]['code']
self.crs = 'EPSG:' + str(results[0]['id']['code'])
self.name = results[0]['name']

def updEnum(self, context):
crsItems = []
if self.results != '':
for result in json.loads(self.results):
srid = 'EPSG:' + result['code']
crsItems.append( (result['code'], result['name'], srid) )
srid = 'EPSG:' + str(result['id']['code'])
crsItems.append( (str(result['id']['code']), result['name'], srid) )
return crsItems

def fill(self, context):
Expand Down