diff --git a/opendm/tiles/gdal2tiles.py b/opendm/tiles/gdal2tiles.py index 081c335a5..f12122756 100644 --- a/opendm/tiles/gdal2tiles.py +++ b/opendm/tiles/gdal2tiles.py @@ -51,6 +51,8 @@ from osgeo import gdal from osgeo import osr +_EPSG_SRS_CACHE = {} + try: from PIL import Image import numpy @@ -209,7 +211,6 @@ def __init__(self, tileSize=256): self.initialResolution = 2 * math.pi * 6378137 / self.tileSize # 156543.03392804062 for tileSize 256 pixels self.originShift = 2 * math.pi * 6378137 / 2.0 - # 20037508.342789244 def LatLonToMeters(self, lat, lon): "Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:3857" @@ -223,10 +224,11 @@ def LatLonToMeters(self, lat, lon): def MetersToLatLon(self, mx, my): "Converts XY point from Spherical Mercator EPSG:3857 to lat/lon in WGS84 Datum" - lon = (mx / self.originShift) * 180.0 - lat = (my / self.originShift) * 180.0 - - lat = 180 / math.pi * (2 * math.atan(math.exp(lat * math.pi / 180.0)) - math.pi / 2.0) + inv_originShift = 180.0 / self.originShift + lon = mx * inv_originShift + lat = my * inv_originShift + pi = math.pi + lat = 180.0 / pi * (2.0 * math.atan(math.exp(lat * pi / 180.0)) - pi / 2.0) return lat, lon def PixelsToMeters(self, px, py, zoom): @@ -712,12 +714,14 @@ def setup_output_srs(input_srs, options): """ Setup the desired SRS (based on options) """ - output_srs = osr.SpatialReference() - - if options.profile == 'mercator': - output_srs.ImportFromEPSG(3857) - elif options.profile == 'geodetic': - output_srs.ImportFromEPSG(4326) + profile = options.profile + + # Use cached instances for standard profiles; else copy input_srs + if profile == 'mercator': + # Use a clone to avoid potential mutation of cached SRS + output_srs = _get_epsg_srs(3857).Clone() + elif profile == 'geodetic': + output_srs = _get_epsg_srs(4326).Clone() else: output_srs = input_srs @@ -2456,25 +2460,29 @@ def generate_openlayers(self): title, bingkey, north, south, east, west, minzoom, maxzoom, tilesize, tileformat, publishurl """ - args = {} - args['title'] = self.options.title - args['bingkey'] = self.options.bingkey - args['south'], args['west'], args['north'], args['east'] = self.swne - args['minzoom'] = self.tminz - args['maxzoom'] = self.tmaxz - args['tilesize'] = self.tilesize - args['tileformat'] = self.tileext - args['publishurl'] = self.options.url - args['copyright'] = self.options.copyright - if self.options.tmscompatible: - args['tmsoffset'] = "-1" - else: - args['tmsoffset'] = "" + args = { + 'title': self.options.title, + 'bingkey': self.options.bingkey, + 'south': self.swne[0], 'west': self.swne[1], 'north': self.swne[2], 'east': self.swne[3], + 'minzoom': self.tminz, + 'maxzoom': self.tmaxz, + 'tilesize': self.tilesize, + 'tileformat': self.tileext, + 'publishurl': self.options.url, + 'copyright': self.options.copyright, + } + args['tmsoffset'] = "-1" if self.options.tmscompatible else "" + + # Pre-calculate raster parameters for profile == 'raster' if self.options.profile == 'raster': - args['rasterzoomlevels'] = self.tmaxz+1 + args['rasterzoomlevels'] = self.tmaxz + 1 args['rastermaxresolution'] = 2**(self.nativezoom) * self.out_gt[1] - s = r""" + # Build HTML string blocks efficiently using a list and join + html_parts = [] + + # Top level styles/header + html_parts.append(r"""