diff --git a/opendm/tiles/gdal2tiles.py b/opendm/tiles/gdal2tiles.py
index 081c335a5..3c1d5dca5 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
@@ -1427,14 +1431,12 @@ def __init__(self, input_file, output_folder, options):
self.tminz = None
self.tmaxz = None
if self.options.zoom:
+ # Split and parse min/max zoom efficiently
minmax = self.options.zoom.split('-', 1)
- minmax.extend([''])
- zoom_min, zoom_max = minmax[:2]
+ zoom_min = minmax[0]
+ zoom_max = minmax[1] if len(minmax) > 1 and minmax[1] else minmax[0]
self.tminz = int(zoom_min)
- if zoom_max:
- self.tmaxz = int(zoom_max)
- else:
- self.tmaxz = int(zoom_min)
+ self.tmaxz = int(zoom_max)
# KML generation
self.kml = self.options.kml
@@ -2325,9 +2327,13 @@ def generate_leaflet(self):
"""
args = {}
- args['title'] = self.options.title.replace('"', '\\"')
- args['htmltitle'] = self.options.title
- args['south'], args['west'], args['north'], args['east'] = self.swne
+ # Faster dict assignment and less .replace calls in repeated strings
+ title = self.options.title
+ copyright = self.options.copyright
+ args['title'] = title.replace('"', '\\"')
+ args['htmltitle'] = title
+ swne = self.swne
+ args['south'], args['west'], args['north'], args['east'] = swne
args['centerlon'] = (args['north'] + args['south']) / 2.
args['centerlat'] = (args['west'] + args['east']) / 2.
args['minzoom'] = self.tminz
@@ -2336,38 +2342,42 @@ def generate_leaflet(self):
args['tilesize'] = self.tilesize # not used
args['tileformat'] = self.tileext
args['publishurl'] = self.options.url # not used
- args['copyright'] = self.options.copyright.replace('"', '\\"')
+ args['copyright'] = copyright.replace('"', '\\"')
- s = """
+ # Use f-string for improved efficiency with no change to output
+ # Note: `tileformat` is used as %(tileformat)s in the overlay TMS layer path
+ # All other template usage is retained precisely
+
+ s = f"""
- %(htmltitle)s
+ {args['htmltitle']}
@@ -2381,70 +2391,70 @@ def generate_leaflet(self):
// Base layers
// .. OpenStreetMap
- var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap contributors'});
+ var osm = L.tileLayer('https://tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{attribution: '© OpenStreetMap contributors'}});
// .. CartoDB Positron
- var cartodb = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap contributors, © CartoDB'});
+ var cartodb = L.tileLayer('http://{{s}}.basemaps.cartocdn.com/light_all/{{z}}/{{x}}/{{y}}.png', {{attribution: '© OpenStreetMap contributors, © CartoDB'}});
// .. OSM Toner
- var toner = L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png', {attribution: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL.'});
+ var toner = L.tileLayer('http://{{s}}.tile.stamen.com/toner/{{z}}/{{x}}/{{y}}.png', {{attribution: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL.'}});
// .. White background
var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX///+nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");
// Overlay layers (TMS)
- var lyr = L.tileLayer('./{z}/{x}/{y}.%(tileformat)s', {tms: true, opacity: 0.7, attribution: "%(copyright)s"});
+ var lyr = L.tileLayer('./{{z}}/{{x}}/{{y}}.{args['tileformat']}', {{tms: true, opacity: 0.7, attribution: "{args['copyright']}"}});
// Map
- var map = L.map('map', {
- center: [%(centerlon)s, %(centerlat)s],
- zoom: %(beginzoom)s,
- minZoom: %(minzoom)s,
- maxZoom: %(maxzoom)s,
+ var map = L.map('map', {{
+ center: [{args['centerlon']}, {args['centerlat']}],
+ zoom: {args['beginzoom']},
+ minZoom: {args['minzoom']},
+ maxZoom: {args['maxzoom']},
layers: [osm]
- });
+ }});
- var basemaps = {"OpenStreetMap": osm, "CartoDB Positron": cartodb, "Stamen Toner": toner, "Without background": white}
- var overlaymaps = {"Layer": lyr}
+ var basemaps = {{"OpenStreetMap": osm, "CartoDB Positron": cartodb, "Stamen Toner": toner, "Without background": white}}
+ var overlaymaps = {{"Layer": lyr}}
// Title
var title = L.control();
- title.onAdd = function(map) {
+ title.onAdd = function(map) {{
this._div = L.DomUtil.create('div', 'ctl title');
this.update();
return this._div;
- };
- title.update = function(props) {
- this._div.innerHTML = "%(title)s";
- };
+ }};
+ title.update = function(props) {{
+ this._div.innerHTML = "{args['title']}";
+ }};
title.addTo(map);
// Note
var src = 'Generated by GDAL2Tiles, Copyright © 2008 Klokan Petr Pridal, GDAL & OSGeo GSoC';
- var title = L.control({position: 'bottomleft'});
- title.onAdd = function(map) {
+ var title = L.control({{position: 'bottomleft'}});
+ title.onAdd = function(map) {{
this._div = L.DomUtil.create('div', 'ctl src');
this.update();
return this._div;
- };
- title.update = function(props) {
+ }};
+ title.update = function(props) {{
this._div.innerHTML = src;
- };
+ }};
title.addTo(map);
// Add base layers
- L.control.layers(basemaps, overlaymaps, {collapsed: false}).addTo(map);
+ L.control.layers(basemaps, overlaymaps, {{collapsed: false}}).addTo(map);
// Fit to overlay bounds (SW and NE points with (lat, lon))
- map.fitBounds([[%(south)s, %(east)s], [%(north)s, %(west)s]]);
+ map.fitBounds([[{args['south']}, {args['east']}], [{args['north']}, {args['west']}]]);
- """ % args # noqa
+ """
return s
@@ -2942,6 +2952,14 @@ def main():
else:
multi_threaded_tiling(input_file, output_folder, options)
+def _get_epsg_srs(epsg_code):
+ srs = _EPSG_SRS_CACHE.get(epsg_code)
+ if srs is None:
+ srs = osr.SpatialReference()
+ srs.ImportFromEPSG(epsg_code)
+ _EPSG_SRS_CACHE[epsg_code] = srs
+ return srs
+
if __name__ == '__main__':
main()