1515
1616class Nik4Image :
1717
18- def __init__ (self ):
18+ def __init__ (self , options ):
19+ self .options = options
1920 self .need_cairo = False
2021 self .fmt = None
22+ self .ppmm = None
2123 self .scale = None
2224 self .scale_factor = None
2325 self .size = None
@@ -26,132 +28,166 @@ def __init__(self):
2628 self .proj_target = None
2729 self .rotate = None
2830
29- @staticmethod
30- def setup_options (options ):
31- settings = Nik4Image ()
31+
32+ def parse_url (url , options ):
33+ """Parse map URL into options map"""
34+ lat = None
35+ lon = None
36+ zoom = None
37+ m = re .search (r'[#/=]([0-9]{1,2})/(-?[0-9]{1,2}\.[0-9]+)/(-?[0-9]{1,3}\.[0-9]+)' , url )
38+ if m :
39+ zoom = int (m .group (1 ))
40+ lat = float (m .group (2 ))
41+ lon = float (m .group (3 ))
42+ else :
43+ m = re .search (r'lat=(-[0-9]{1,2}\.[0-9]+)' , url , flags = re .IGNORECASE )
44+ if m :
45+ lat = float (m .group (1 ))
46+ m = re .search (r'lon=(-[0-9]{1,3}\.[0-9]+)' , url , flags = re .IGNORECASE )
47+ if m :
48+ lon = float (m .group (1 ))
49+ m = re .search (r'zoom=([0-9]{1,2})' , url , flags = re .IGNORECASE )
50+ if m :
51+ zoom = int (m .group (1 ))
52+ if zoom and not options .zoom :
53+ self .options .zoom = zoom
54+ if lat and lon and not self .options .center :
55+ self .options .center = [lon , lat ]
56+ if (not self .options .size and not self .options .size_px and not self .options .paper
57+ and not self .options .fit and not self .options .bbox ):
58+ self .options .size_px = [1280 , 1024 ]
59+
60+ def _set_format (self ):
61+ # format should not be empty
62+ if self .options .fmt :
63+ self .fmt = self .options .fmt .lower ()
64+ elif '.' in self .options .output :
65+ self .fmt = self .options .output .split ('.' )[- 1 ].lower ()
66+ else :
67+ self .fmt = 'png256'
68+
69+ self .need_cairo = self .fmt in ['svg' , 'pdf' ]
70+
71+ def _set_ppm_and_scale_factor (self ):
72+ """
73+ Set properties ppm and scale_factor.
74+ """
75+ # ppi and scale factor are the same thing
76+ if self .options .ppi :
77+ self .ppmm = self .options .ppi / 25.4
78+ self .scale_factor = self .options .ppi / 90.7
79+ else :
80+ self .scale_factor = self .options .factor
81+ self .ppmm = 90.7 / 25.4 * self .scale_factor
82+
83+ # svg / pdf can be scaled only in cairo mode
84+ if self .scale_factor != 1 and self .need_cairo and not HAS_CAIRO :
85+ logging .error ('Warning: install pycairo for using --factor or --ppi' )
86+ self .scale_factor = 1
87+ self .ppmm = 90.7 / 25.4
88+
89+ def setup_options (self ):
3290 dim_mm = None
33- settings .rotate = not options .norotate
91+ self .rotate = not self . options .norotate
3492
35- if (options .ozi and options .projection .lower () != 'epsg:3857'
36- and options .projection != EPSG_3857 ):
93+ if (self . options .ozi and self . options .projection .lower () != 'epsg:3857'
94+ and self . options .projection != EPSG_3857 ):
3795 raise Exception ('Ozi map file output is only supported for Web Mercator (EPSG:3857). ' +
3896 'Please remove --projection.' )
3997
40- if options .url :
41- parse_url (options .url , options )
42-
43- # format should not be empty
44- if options .fmt :
45- settings .fmt = options .fmt .lower ()
46- elif '.' in options .output :
47- settings .fmt = options .output .split ('.' )[- 1 ].lower ()
48- else :
49- settings .fmt = 'png256'
98+ self ._set_format ()
5099
51- settings .need_cairo = settings .fmt in ['svg' , 'pdf' ]
100+ if self .options .url :
101+ parse_url (self .options .url , self .options )
52102
53103 # output projection
54- if options .projection .isdigit ():
55- settings .proj_target = mapnik .Projection ('+init=epsg:{}' .format (options .projection ))
104+ if self . options .projection .isdigit ():
105+ self .proj_target = mapnik .Projection ('+init=epsg:{}' .format (self . options .projection ))
56106 else :
57- settings .proj_target = mapnik .Projection (options .projection )
58- settings .transform = mapnik .ProjTransform (PROJ_LONLAT , settings .proj_target )
107+ self .proj_target = mapnik .Projection (self . options .projection )
108+ self .transform = mapnik .ProjTransform (PROJ_LONLAT , self .proj_target )
59109
60110 # get image size in millimeters
61- if options .paper :
111+ if self . options .paper :
62112 portrait = False
63- if options .paper [0 ] == '-' :
113+ if self . options .paper [0 ] == '-' :
64114 portrait = True
65- settings .rotate = False
66- options .paper = options .paper [1 :]
67- elif options .paper [0 ] == '+' :
68- settings .rotate = False
69- options .paper = options .paper [1 :]
115+ self .rotate = False
116+ self . options .paper = self . options .paper [1 :]
117+ elif self . options .paper [0 ] == '+' :
118+ self .rotate = False
119+ self . options .paper = self . options .paper [1 :]
70120 else :
71- settings .rotate = True
72- dim_mm = get_paper_size (options .paper .lower ())
121+ self .rotate = True
122+ dim_mm = get_paper_size (self . options .paper .lower ())
73123 if not dim_mm :
74- raise Exception ('Incorrect paper format: ' + options .paper )
124+ raise Exception ('Incorrect paper format: ' + self . options .paper )
75125 if portrait :
76126 dim_mm = [dim_mm [1 ], dim_mm [0 ]]
77- elif options .size :
78- dim_mm = options .size
79- if dim_mm and options .margin :
80- dim_mm [0 ] = max (0 , dim_mm [0 ] - options .margin * 2 )
81- dim_mm [1 ] = max (0 , dim_mm [1 ] - options .margin * 2 )
127+ elif self . options .size :
128+ dim_mm = self . options .size
129+ if dim_mm and self . options .margin :
130+ dim_mm [0 ] = max (0 , dim_mm [0 ] - self . options .margin * 2 )
131+ dim_mm [1 ] = max (0 , dim_mm [1 ] - self . options .margin * 2 )
82132
83- # ppi and scale factor are the same thing
84- if options .ppi :
85- ppmm = options .ppi / 25.4
86- settings .scale_factor = options .ppi / 90.7
87- else :
88- settings .scale_factor = options .factor
89- ppmm = 90.7 / 25.4 * settings .scale_factor
90-
91- # svg / pdf can be scaled only in cairo mode
92- if settings .scale_factor != 1 and settings .need_cairo and not HAS_CAIRO :
93- logging .error ('Warning: install pycairo for using --factor or --ppi' )
94- settings .scale_factor = 1
95- ppmm = 90.7 / 25.4
133+ self ._set_ppm_and_scale_factor ()
96134
97135 # convert physical size to pixels
98- if options .size_px :
99- settings .size = options .size_px
136+ if self . options .size_px :
137+ self .size = self . options .size_px
100138 elif dim_mm :
101- settings .size = [int (round (dim_mm [0 ] * ppmm )), int (round (dim_mm [1 ] * ppmm ))]
139+ self .size = [int (round (dim_mm [0 ] * self . ppmm )), int (round (dim_mm [1 ] * self . ppmm ))]
102140
103- if settings .size and settings .size [0 ] + settings .size [1 ] <= 0 :
141+ if self .size and self .size [0 ] + self .size [1 ] <= 0 :
104142 raise Exception ('Both dimensions are less or equal to zero' )
105143
106- if options .bbox :
107- settings .bbox = options .bbox
144+ if self . options .bbox :
145+ self .bbox = self . options .bbox
108146
109147 # scale can be specified with zoom or with 1:NNN scale
110148 fix_scale = False
111- if options .zoom :
112- settings .scale = 2 * 3.14159 * 6378137 / 2 ** (options .zoom + 8 ) / settings .scale_factor
113- elif options .scale :
114- settings .scale = options .scale * 0.00028 / settings .scale_factor
149+ if self . options .zoom :
150+ self .scale = 2 * 3.14159 * 6378137 / 2 ** (self . options .zoom + 8 ) / self .scale_factor
151+ elif self . options .scale :
152+ self .scale = self . options .scale * 0.00028 / self .scale_factor
115153 # Now we have to divide by cos(lat), but we might not know latitude at this point
116154 # TODO: division should only happen for EPSG:3857 or not at all
117- if options .center :
118- settings .scale = settings .scale / math .cos (math .radians (options .center [1 ]))
119- elif options .bbox :
120- settings .scale = settings .scale / math .cos (math .radians ((options .bbox [3 ] + options .bbox [1 ]) / 2 ))
155+ if self . options .center :
156+ self .scale = self .scale / math .cos (math .radians (self . options .center [1 ]))
157+ elif self . options .bbox :
158+ self .scale = self .scale / math .cos (math .radians ((self . options .bbox [3 ] + self . options .bbox [1 ]) / 2 ))
121159 else :
122160 fix_scale = True
123161
124162 # all calculations are in EPSG:3857 projection (it's easier)
125- if settings .bbox :
126- settings .bbox = settings .transform .forward (mapnik .Box2d (* settings .bbox ))
127- bbox_web_merc = TRANSFORM_LONLAT_WEBMERC .forward (mapnik .Box2d (* (options .bbox )))
128- if settings .scale :
129- settings . scale = Nik4Image . correct_scale (settings . bbox , settings . scale , bbox_web_merc , settings . bbox )
163+ if self .bbox :
164+ self .bbox = self .transform .forward (mapnik .Box2d (* self .bbox ))
165+ bbox_web_merc = TRANSFORM_LONLAT_WEBMERC .forward (mapnik .Box2d (* (self . options .bbox )))
166+ if self .scale :
167+ self . correct_scale (bbox_web_merc )
130168
131169 # calculate bbox through center, zoom and target size
132- if not settings .bbox and options .center and settings .size and settings .size [0 ] > 0 and settings .size [1 ] > 0 and settings .scale :
170+ if not self .bbox and self . options .center and self .size and self .size [0 ] > 0 and self .size [1 ] > 0 and self .scale :
133171 # We don't know over which latitude range the bounding box spans, so we
134172 # first do everything in Web Mercator.
135- center = TRANSFORM_LONLAT_WEBMERC .forward (mapnik .Coord (* options .center ))
136- w = settings .size [0 ] * settings .scale / 2
137- h = settings .size [1 ] * settings .scale / 2
173+ center = TRANSFORM_LONLAT_WEBMERC .forward (mapnik .Coord (* self . options .center ))
174+ w = self .size [0 ] * self .scale / 2
175+ h = self .size [1 ] * self .scale / 2
138176 bbox_web_merc = mapnik .Box2d (center .x - w , center .y - h , center .x + w , center .y + h )
139- settings .bbox = TRANSFORM_LONLAT_WEBMERC .backward (bbox_web_merc )
140- settings .bbox = settings .transform .forward (settings .bbox )
177+ self .bbox = TRANSFORM_LONLAT_WEBMERC .backward (bbox_web_merc )
178+ self .bbox = self .transform .forward (self .bbox )
141179 # now correct the scale
142- settings .scale = Nik4Image .correct_scale (settings .bbox , settings .scale , bbox_web_merc , settings .bbox )
143- center = settings .transform .forward (mapnik .Coord (* options .center ))
144- w = settings .size [0 ] * settings .scale / 2
145- h = settings .size [1 ] * settings .scale / 2
146- settings .bbox = mapnik .Box2d (center .x - w , center .y - h , center .x + w , center .y + h )
147- return settings
180+ self .correct_scale (bbox_web_merc )
181+ center = self .transform .forward (mapnik .Coord (* self .options .center ))
182+ w = self .size [0 ] * self .scale / 2
183+ h = self .size [1 ] * self .scale / 2
184+ self .bbox = mapnik .Box2d (center .x - w , center .y - h , center .x + w , center .y + h )
148185
149- @staticmethod
150- def correct_scale (bbox , scale , bbox_web_merc , bbox_target ):
186+ def correct_scale (self , bbox_web_merc ):
151187 # correct scale if output projection is not EPSG:3857
152188 x_dist_merc = bbox_web_merc .maxx - bbox_web_merc .minx
153- x_dist_target = bbox .maxx - bbox .minx
154- return scale * (x_dist_target / x_dist_merc )
189+ x_dist_target = self . bbox .maxx - self . bbox .minx
190+ self . scale = self . scale * (x_dist_target / x_dist_merc )
155191
156192 @staticmethod
157193 def get_argument_parser ():
0 commit comments