/src/MapServer/src/mapscale.c
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Scale object rendering. |
6 | | * Author: Steve Lime and the MapServer team. |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
10 | | * |
11 | | * Permission is hereby granted, free of charge, to any person obtaining a |
12 | | * copy of this software and associated documentation files (the "Software"), |
13 | | * to deal in the Software without restriction, including without limitation |
14 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
15 | | * and/or sell copies of the Software, and to permit persons to whom the |
16 | | * Software is furnished to do so, subject to the following conditions: |
17 | | * |
18 | | * The above copyright notice and this permission notice shall be included in |
19 | | * all copies of this Software or works derived from this Software. |
20 | | * |
21 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
22 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
24 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
26 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
27 | | * DEALINGS IN THE SOFTWARE. |
28 | | ****************************************************************************/ |
29 | | |
30 | | #include "mapserver.h" |
31 | | |
32 | 0 | #define VMARGIN 3 /* buffer around the scalebar */ |
33 | 0 | #define HMARGIN 3 |
34 | | #define VSPACING .8 /* spacing (% of font height) between scalebar and text */ |
35 | 0 | #define VSLOP 5 /* makes things fit a bit better vertically */ |
36 | | |
37 | | /* |
38 | | ** Match this with with unit enumerations is mapserver.h |
39 | | */ |
40 | | static char *unitText[9] = { |
41 | | "in", "ft", "mi", "m", "km", |
42 | | "dd", "??", "??", "NM"}; /* MS_PIXEL and MS_PERCENTAGE not used */ |
43 | | double inchesPerUnit[9] = {1, 12, 63360.0, 39.3701, 39370.1, |
44 | | 4374754, 1, 1, 72913.3858}; |
45 | | |
46 | 0 | static double roundInterval(double d) { |
47 | 0 | if (d < .001) |
48 | 0 | return (MS_NINT(d * 10000) / 10000.0); |
49 | 0 | if (d < .01) |
50 | 0 | return (MS_NINT(d * 1000) / 1000.0); |
51 | 0 | if (d < .1) |
52 | 0 | return (MS_NINT(d * 100) / 100.0); |
53 | 0 | if (d < 1) |
54 | 0 | return (MS_NINT(d * 10) / 10.0); |
55 | 0 | if (d < 100) |
56 | 0 | return (MS_NINT(d)); |
57 | 0 | if (d < 1000) |
58 | 0 | return (MS_NINT(d / 10) * 10); |
59 | 0 | if (d < 10000) |
60 | 0 | return (MS_NINT(d / 100) * 100); |
61 | 0 | if (d < 100000) |
62 | 0 | return (MS_NINT(d / 1000) * 1000); |
63 | 0 | if (d < 1000000) |
64 | 0 | return (MS_NINT(d / 10000) * 10000); |
65 | 0 | if (d < 10000000) |
66 | 0 | return (MS_NINT(d / 100000) * 100000); |
67 | 0 | if (d < 100000000) |
68 | 0 | return (MS_NINT(d / 1000000) * 1000000); |
69 | | |
70 | 0 | return (-1); |
71 | 0 | } |
72 | | |
73 | | /* |
74 | | ** Calculate the approximate scale based on a few parameters. Note that this |
75 | | *assumes the scale is |
76 | | ** the same in the x direction as in the y direction, so run msAdjustExtent(...) |
77 | | *first. |
78 | | */ |
79 | | int msCalculateScale(rectObj extent, int units, int width, int height, |
80 | 0 | double resolution, double *scale) { |
81 | 0 | double md, gd, center_y; |
82 | | |
83 | | /* if((extent.maxx == extent.minx) || (extent.maxy == extent.miny)) */ |
84 | 0 | if (!MS_VALID_EXTENT(extent)) { |
85 | 0 | msSetError(MS_MISCERR, |
86 | 0 | "Invalid image extent, minx=%lf, miny=%lf, maxx=%lf, maxy=%lf.", |
87 | 0 | "msCalculateScale()", extent.minx, extent.miny, extent.maxx, |
88 | 0 | extent.maxy); |
89 | 0 | return (MS_FAILURE); |
90 | 0 | } |
91 | | |
92 | 0 | if ((width <= 0) || (height <= 0)) { |
93 | 0 | msSetError(MS_MISCERR, "Invalid image width or height.", |
94 | 0 | "msCalculateScale()"); |
95 | 0 | return (MS_FAILURE); |
96 | 0 | } |
97 | | |
98 | 0 | switch (units) { |
99 | 0 | case (MS_DD): |
100 | 0 | case (MS_METERS): |
101 | 0 | case (MS_KILOMETERS): |
102 | 0 | case (MS_MILES): |
103 | 0 | case (MS_NAUTICALMILES): |
104 | 0 | case (MS_INCHES): |
105 | 0 | case (MS_FEET): |
106 | 0 | center_y = (extent.miny + extent.maxy) / 2.0; |
107 | 0 | md = (width - 1) / |
108 | 0 | (resolution * |
109 | 0 | msInchesPerUnit( |
110 | 0 | units, center_y)); /* remember, we use a pixel-center to |
111 | | pixel-center extent, hence the width-1 */ |
112 | 0 | gd = extent.maxx - extent.minx; |
113 | 0 | *scale = gd / md; |
114 | 0 | break; |
115 | 0 | default: |
116 | 0 | *scale = -1; /* this is not an error */ |
117 | 0 | break; |
118 | 0 | } |
119 | | |
120 | 0 | return (MS_SUCCESS); |
121 | 0 | } |
122 | | |
123 | 0 | double msInchesPerUnit(int units, double center_lat) { |
124 | 0 | (void)center_lat; |
125 | 0 | double lat_adj = 1.0, ipu = 1.0; |
126 | |
|
127 | 0 | switch (units) { |
128 | 0 | case (MS_METERS): |
129 | 0 | case (MS_KILOMETERS): |
130 | 0 | case (MS_MILES): |
131 | 0 | case (MS_NAUTICALMILES): |
132 | 0 | case (MS_INCHES): |
133 | 0 | case (MS_FEET): |
134 | 0 | ipu = inchesPerUnit[units]; |
135 | 0 | break; |
136 | 0 | case (MS_DD): |
137 | | /* With geographical (DD) coordinates, we adjust the inchesPerUnit |
138 | | * based on the latitude of the center of the view. For this we assume |
139 | | * we have a perfect sphere and just use cos(lat) in our calculation. |
140 | | */ |
141 | | #ifdef ENABLE_VARIABLE_INCHES_PER_DEGREE |
142 | | if (center_lat != 0.0) { |
143 | | double cos_lat; |
144 | | cos_lat = cos(MS_PI * center_lat / 180.0); |
145 | | lat_adj = sqrt(1 + cos_lat * cos_lat) / sqrt(2.0); |
146 | | } |
147 | | #endif |
148 | 0 | ipu = inchesPerUnit[units] * lat_adj; |
149 | 0 | break; |
150 | 0 | default: |
151 | 0 | break; |
152 | 0 | } |
153 | | |
154 | 0 | return ipu; |
155 | 0 | } |
156 | | |
157 | 0 | #define X_STEP_SIZE 5 |
158 | | |
159 | 0 | imageObj *msDrawScalebar(mapObj *map) { |
160 | 0 | int status; |
161 | 0 | char label[32]; |
162 | 0 | double i, msx; |
163 | 0 | int j; |
164 | 0 | int isx, sx, sy, ox, oy, state, dsx; |
165 | 0 | pointObj p; |
166 | 0 | rectObj r; |
167 | 0 | imageObj *image = NULL; |
168 | 0 | double fontWidth, fontHeight; |
169 | 0 | outputFormatObj *format = NULL; |
170 | 0 | strokeStyleObj strokeStyle = {0}; |
171 | 0 | shapeObj shape; |
172 | 0 | lineObj line; |
173 | 0 | pointObj points[5]; |
174 | 0 | textSymbolObj ts; |
175 | 0 | rendererVTableObj *renderer; |
176 | |
|
177 | 0 | strokeStyle.patternlength = 0; |
178 | 0 | initTextSymbol(&ts); |
179 | |
|
180 | 0 | if ((int)map->units == -1) { |
181 | 0 | msSetError(MS_MISCERR, "Map units not set.", "msDrawScalebar()"); |
182 | 0 | return (NULL); |
183 | 0 | } |
184 | | |
185 | 0 | renderer = MS_MAP_RENDERER(map); |
186 | 0 | if (!renderer || !MS_MAP_RENDERER(map)->supports_pixel_buffer) { |
187 | 0 | msSetError(MS_MISCERR, "Outputformat not supported for scalebar", |
188 | 0 | "msDrawScalebar()"); |
189 | 0 | return (NULL); |
190 | 0 | } |
191 | | |
192 | 0 | msPopulateTextSymbolForLabelAndString( |
193 | 0 | &ts, &map->scalebar.label, msStrdup("0123456789"), 1.0, |
194 | 0 | map->resolution / map->defresolution, 0); |
195 | | |
196 | | /* |
197 | | * A string containing the ten decimal digits is rendered to compute an |
198 | | * average cell size for each number, which is used later to place labels on |
199 | | * the scalebar. |
200 | | */ |
201 | |
|
202 | 0 | if (msGetTextSymbolSize(map, &ts, &r) != MS_SUCCESS) { |
203 | 0 | return NULL; |
204 | 0 | } |
205 | 0 | fontWidth = (r.maxx - r.minx) / 10.0; |
206 | 0 | fontHeight = r.maxy - r.miny; |
207 | |
|
208 | 0 | map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height); |
209 | 0 | status = msCalculateScale(map->extent, map->units, map->width, map->height, |
210 | 0 | map->resolution, &map->scaledenom); |
211 | 0 | if (status != MS_SUCCESS) { |
212 | 0 | return (NULL); |
213 | 0 | } |
214 | 0 | dsx = map->scalebar.width - 2 * HMARGIN; |
215 | 0 | do { |
216 | 0 | msx = (map->cellsize * dsx) / (msInchesPerUnit(map->scalebar.units, 0) / |
217 | 0 | msInchesPerUnit(map->units, 0)); |
218 | 0 | i = roundInterval(msx / map->scalebar.intervals); |
219 | 0 | snprintf(label, sizeof(label), "%g", |
220 | 0 | map->scalebar.intervals * i); /* last label */ |
221 | 0 | isx = MS_NINT((i / (msInchesPerUnit(map->units, 0) / |
222 | 0 | msInchesPerUnit(map->scalebar.units, 0))) / |
223 | 0 | map->cellsize); |
224 | 0 | sx = (map->scalebar.intervals * isx) + |
225 | 0 | MS_NINT((1.5 + strlen(label) / 2.0 + |
226 | 0 | strlen(unitText[map->scalebar.units])) * |
227 | 0 | fontWidth); |
228 | |
|
229 | 0 | if (sx <= (map->scalebar.width - 2 * HMARGIN)) |
230 | 0 | break; /* it will fit */ |
231 | | |
232 | 0 | dsx -= X_STEP_SIZE; /* change the desired size in hopes that it will fit in |
233 | | user supplied width */ |
234 | 0 | } while (1); |
235 | |
|
236 | 0 | sy = (2 * VMARGIN) + MS_NINT(VSPACING * fontHeight) + fontHeight + |
237 | 0 | map->scalebar.height - VSLOP; |
238 | | |
239 | | /*Ensure we have an image format representing the options for the scalebar.*/ |
240 | 0 | msApplyOutputFormat(&format, map->outputformat, map->scalebar.transparent); |
241 | |
|
242 | 0 | if (map->scalebar.transparent == MS_OFF) { |
243 | 0 | if (!MS_VALID_COLOR(map->scalebar.imagecolor)) |
244 | 0 | MS_INIT_COLOR(map->scalebar.imagecolor, 255, 255, 255, 255); |
245 | 0 | } |
246 | 0 | image = msImageCreate(map->scalebar.width, sy, format, map->web.imagepath, |
247 | 0 | map->web.imageurl, map->resolution, map->defresolution, |
248 | 0 | &map->scalebar.imagecolor); |
249 | | |
250 | | /* did we succeed in creating the image? */ |
251 | 0 | if (!image) { |
252 | 0 | msSetError(MS_MISCERR, "Unable to initialize image.", "msDrawScalebar()"); |
253 | 0 | return NULL; |
254 | 0 | } |
255 | 0 | image->map = map; |
256 | | |
257 | | /* drop this reference to output format */ |
258 | 0 | msApplyOutputFormat(&format, NULL, MS_NOOVERRIDE); |
259 | |
|
260 | 0 | switch (map->scalebar.align) { |
261 | 0 | case (MS_ALIGN_LEFT): |
262 | 0 | ox = HMARGIN; |
263 | 0 | break; |
264 | 0 | case (MS_ALIGN_RIGHT): |
265 | 0 | ox = MS_NINT((map->scalebar.width - sx) + fontWidth); |
266 | 0 | break; |
267 | 0 | default: |
268 | 0 | ox = MS_NINT((map->scalebar.width - sx) / 2.0 + |
269 | 0 | fontWidth / 2.0); /* center the computed scalebar */ |
270 | 0 | } |
271 | 0 | oy = VMARGIN; |
272 | |
|
273 | 0 | switch (map->scalebar.style) { |
274 | 0 | case (0): { |
275 | |
|
276 | 0 | line.numpoints = 5; |
277 | 0 | line.point = points; |
278 | 0 | shape.line = &line; |
279 | 0 | shape.numlines = 1; |
280 | 0 | if (MS_VALID_COLOR(map->scalebar.color)) { |
281 | 0 | INIT_STROKE_STYLE(strokeStyle); |
282 | 0 | strokeStyle.color = &map->scalebar.outlinecolor; |
283 | 0 | strokeStyle.color->alpha = 255; |
284 | 0 | strokeStyle.width = 1; |
285 | 0 | } |
286 | 0 | map->scalebar.backgroundcolor.alpha = 255; |
287 | 0 | map->scalebar.color.alpha = 255; |
288 | 0 | state = 1; /* 1 means filled */ |
289 | 0 | for (j = 0; j < map->scalebar.intervals; j++) { |
290 | 0 | points[0].x = points[4].x = points[3].x = ox + j * isx + 0.5; |
291 | 0 | points[0].y = points[4].y = points[1].y = oy + 0.5; |
292 | 0 | points[1].x = points[2].x = ox + (j + 1) * isx + 0.5; |
293 | 0 | points[2].y = points[3].y = oy + map->scalebar.height + 0.5; |
294 | 0 | if (state == 1 && MS_VALID_COLOR(map->scalebar.color)) |
295 | 0 | status = renderer->renderPolygon(image, &shape, &map->scalebar.color); |
296 | 0 | else if (MS_VALID_COLOR(map->scalebar.backgroundcolor)) |
297 | 0 | status = renderer->renderPolygon(image, &shape, |
298 | 0 | &map->scalebar.backgroundcolor); |
299 | |
|
300 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
301 | 0 | goto scale_cleanup; |
302 | 0 | } |
303 | | |
304 | 0 | if (strokeStyle.color) { |
305 | 0 | status = renderer->renderLine(image, &shape, &strokeStyle); |
306 | |
|
307 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
308 | 0 | goto scale_cleanup; |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | 0 | snprintf(label, sizeof(label), "%g", j * i); |
313 | 0 | map->scalebar.label.position = MS_CC; |
314 | 0 | p.x = ox + j * isx; /* + MS_NINT(fontPtr->w/2); */ |
315 | 0 | p.y = oy + map->scalebar.height + MS_NINT(VSPACING * fontHeight); |
316 | 0 | status = msDrawLabel(map, image, p, msStrdup(label), &map->scalebar.label, |
317 | 0 | 1.0); |
318 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
319 | 0 | goto scale_cleanup; |
320 | 0 | } |
321 | 0 | state = -state; |
322 | 0 | } |
323 | 0 | snprintf(label, sizeof(label), "%g", j * i); |
324 | 0 | ox = ox + j * isx - MS_NINT((strlen(label) * fontWidth) / 2.0); |
325 | 0 | snprintf(label, sizeof(label), "%g %s", j * i, |
326 | 0 | unitText[map->scalebar.units]); |
327 | 0 | map->scalebar.label.position = MS_CR; |
328 | 0 | p.x = ox; /* + MS_NINT(fontPtr->w/2); */ |
329 | 0 | p.y = oy + map->scalebar.height + MS_NINT(VSPACING * fontHeight); |
330 | 0 | status = |
331 | 0 | msDrawLabel(map, image, p, msStrdup(label), &map->scalebar.label, 1.0); |
332 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
333 | 0 | goto scale_cleanup; |
334 | 0 | } |
335 | 0 | break; |
336 | 0 | } |
337 | 0 | case (1): { |
338 | 0 | line.numpoints = 2; |
339 | 0 | line.point = points; |
340 | 0 | shape.line = &line; |
341 | 0 | shape.numlines = 1; |
342 | 0 | if (MS_VALID_COLOR(map->scalebar.color)) { |
343 | 0 | strokeStyle.width = 1; |
344 | 0 | strokeStyle.color = &map->scalebar.color; |
345 | 0 | } |
346 | |
|
347 | 0 | points[0].y = points[1].y = oy; |
348 | 0 | points[0].x = ox; |
349 | 0 | points[1].x = ox + isx * map->scalebar.intervals; |
350 | 0 | status = renderer->renderLine(image, &shape, &strokeStyle); |
351 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
352 | 0 | goto scale_cleanup; |
353 | 0 | } |
354 | | |
355 | 0 | points[0].y = oy; |
356 | 0 | points[1].y = oy + map->scalebar.height; |
357 | 0 | p.y = oy + map->scalebar.height + MS_NINT(VSPACING * fontHeight); |
358 | 0 | for (j = 0; j <= map->scalebar.intervals; j++) { |
359 | 0 | points[0].x = points[1].x = ox + j * isx; |
360 | 0 | status = renderer->renderLine(image, &shape, &strokeStyle); |
361 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
362 | 0 | goto scale_cleanup; |
363 | 0 | } |
364 | | |
365 | 0 | snprintf(label, sizeof(label), "%g", j * i); |
366 | 0 | if (j != map->scalebar.intervals) { |
367 | 0 | map->scalebar.label.position = MS_CC; |
368 | 0 | p.x = ox + j * isx; /* + MS_NINT(fontPtr->w/2); */ |
369 | 0 | } else { |
370 | 0 | snprintf(label, sizeof(label), "%g %s", j * i, |
371 | 0 | unitText[map->scalebar.units]); |
372 | 0 | map->scalebar.label.position = MS_CR; |
373 | 0 | p.x = ox + j * isx - MS_NINT((strlen(label) * fontWidth) / 2.0); |
374 | 0 | } |
375 | 0 | status = msDrawLabel(map, image, p, msStrdup(label), &map->scalebar.label, |
376 | 0 | 1.0); |
377 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
378 | 0 | goto scale_cleanup; |
379 | 0 | } |
380 | 0 | } |
381 | 0 | break; |
382 | 0 | } |
383 | 0 | default: |
384 | 0 | msSetError(MS_MISCERR, "Unsupported scalebar style.", "msDrawScalebar()"); |
385 | 0 | return (NULL); |
386 | 0 | } |
387 | | |
388 | 0 | scale_cleanup: |
389 | 0 | freeTextSymbol(&ts); |
390 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
391 | 0 | msFreeImage(image); |
392 | 0 | return NULL; |
393 | 0 | } |
394 | 0 | return (image); |
395 | 0 | } |
396 | | |
397 | 0 | int msEmbedScalebar(mapObj *map, imageObj *img) { |
398 | 0 | int l, index, s, status = MS_SUCCESS; |
399 | 0 | pointObj point; |
400 | 0 | imageObj *image = NULL; |
401 | 0 | rendererVTableObj *renderer; |
402 | 0 | symbolObj *embeddedSymbol; |
403 | 0 | char *imageType = NULL; |
404 | |
|
405 | 0 | index = msGetSymbolIndex(&(map->symbolset), "scalebar", MS_FALSE); |
406 | 0 | if (index != -1) |
407 | 0 | msRemoveSymbol(&(map->symbolset), |
408 | 0 | index); /* remove cached symbol in case the function is |
409 | | called multiple times with different zoom levels */ |
410 | |
|
411 | 0 | if ((embeddedSymbol = msGrowSymbolSet(&map->symbolset)) == NULL) |
412 | 0 | return MS_FAILURE; |
413 | | |
414 | 0 | s = map->symbolset.numsymbols; |
415 | 0 | map->symbolset.numsymbols++; |
416 | |
|
417 | 0 | if (!MS_RENDERER_PLUGIN(map->outputformat) || |
418 | 0 | !MS_MAP_RENDERER(map)->supports_pixel_buffer) { |
419 | 0 | imageType = msStrdup(map->imagetype); /* save format */ |
420 | 0 | if MS_DRIVER_CAIRO (map->outputformat) |
421 | 0 | map->outputformat = msSelectOutputFormat(map, "cairopng"); |
422 | 0 | else |
423 | 0 | map->outputformat = msSelectOutputFormat(map, "png"); |
424 | |
|
425 | 0 | msInitializeRendererVTable(map->outputformat); |
426 | 0 | } |
427 | 0 | renderer = MS_MAP_RENDERER(map); |
428 | |
|
429 | 0 | image = msDrawScalebar(map); |
430 | |
|
431 | 0 | if (imageType) { |
432 | 0 | map->outputformat = |
433 | 0 | msSelectOutputFormat(map, imageType); /* restore format */ |
434 | 0 | msFree(imageType); |
435 | 0 | } |
436 | |
|
437 | 0 | if (!image) { |
438 | 0 | return MS_FAILURE; |
439 | 0 | } |
440 | 0 | embeddedSymbol->pixmap_buffer = calloc(1, sizeof(rasterBufferObj)); |
441 | 0 | MS_CHECK_ALLOC(embeddedSymbol->pixmap_buffer, sizeof(rasterBufferObj), |
442 | 0 | MS_FAILURE); |
443 | |
|
444 | 0 | if (MS_SUCCESS != |
445 | 0 | renderer->getRasterBufferCopy(image, embeddedSymbol->pixmap_buffer)) { |
446 | 0 | return MS_FAILURE; |
447 | 0 | } |
448 | | |
449 | 0 | embeddedSymbol->type = MS_SYMBOL_PIXMAP; /* initialize a few things */ |
450 | 0 | embeddedSymbol->name = msStrdup("scalebar"); |
451 | 0 | embeddedSymbol->sizex = embeddedSymbol->pixmap_buffer->width; |
452 | 0 | embeddedSymbol->sizey = embeddedSymbol->pixmap_buffer->height; |
453 | 0 | if (map->scalebar.transparent) { |
454 | 0 | embeddedSymbol->transparent = MS_TRUE; |
455 | 0 | embeddedSymbol->transparentcolor = 0; |
456 | 0 | } |
457 | |
|
458 | 0 | switch (map->scalebar.position) { |
459 | 0 | case (MS_LL): |
460 | 0 | point.x = MS_NINT(embeddedSymbol->pixmap_buffer->width / 2.0) + |
461 | 0 | map->scalebar.offsetx; |
462 | 0 | point.y = map->height - |
463 | 0 | MS_NINT(embeddedSymbol->pixmap_buffer->height / 2.0) - |
464 | 0 | map->scalebar.offsety; |
465 | 0 | break; |
466 | 0 | case (MS_LR): |
467 | 0 | point.x = map->width - MS_NINT(embeddedSymbol->pixmap_buffer->width / 2.0) - |
468 | 0 | map->scalebar.offsetx; |
469 | 0 | point.y = map->height - |
470 | 0 | MS_NINT(embeddedSymbol->pixmap_buffer->height / 2.0) - |
471 | 0 | map->scalebar.offsety; |
472 | 0 | break; |
473 | 0 | case (MS_LC): |
474 | 0 | point.x = MS_NINT(map->width / 2.0) + map->scalebar.offsetx; |
475 | 0 | point.y = map->height - |
476 | 0 | MS_NINT(embeddedSymbol->pixmap_buffer->height / 2.0) - |
477 | 0 | map->scalebar.offsety; |
478 | 0 | break; |
479 | 0 | case (MS_UR): |
480 | 0 | point.x = map->width - MS_NINT(embeddedSymbol->pixmap_buffer->width / 2.0) - |
481 | 0 | map->scalebar.offsetx; |
482 | 0 | point.y = MS_NINT(embeddedSymbol->pixmap_buffer->height / 2.0) + |
483 | 0 | map->scalebar.offsety; |
484 | 0 | break; |
485 | 0 | case (MS_UL): |
486 | 0 | point.x = MS_NINT(embeddedSymbol->pixmap_buffer->width / 2.0) + |
487 | 0 | map->scalebar.offsetx; |
488 | 0 | point.y = MS_NINT(embeddedSymbol->pixmap_buffer->height / 2.0) + |
489 | 0 | map->scalebar.offsety; |
490 | 0 | break; |
491 | 0 | case (MS_UC): |
492 | 0 | point.x = MS_NINT(map->width / 2.0) + map->scalebar.offsetx; |
493 | 0 | point.y = MS_NINT(embeddedSymbol->pixmap_buffer->height / 2.0) + |
494 | 0 | map->scalebar.offsety; |
495 | 0 | break; |
496 | 0 | } |
497 | | |
498 | 0 | l = msGetLayerIndex(map, "__embed__scalebar"); |
499 | 0 | if (l == -1) { |
500 | 0 | if (msGrowMapLayers(map) == NULL) |
501 | 0 | return (-1); |
502 | 0 | l = map->numlayers; |
503 | 0 | map->numlayers++; |
504 | 0 | if (initLayer((GET_LAYER(map, l)), map) == -1) |
505 | 0 | return (-1); |
506 | 0 | GET_LAYER(map, l)->name = msStrdup("__embed__scalebar"); |
507 | 0 | GET_LAYER(map, l)->type = MS_LAYER_POINT; |
508 | |
|
509 | 0 | if (msGrowLayerClasses(GET_LAYER(map, l)) == NULL) |
510 | 0 | return (-1); |
511 | | |
512 | 0 | if (initClass(GET_LAYER(map, l)->class[0]) == -1) |
513 | 0 | return (-1); |
514 | 0 | GET_LAYER(map, l)->numclasses = 1; /* so we make sure to free it */ |
515 | | |
516 | | /* update the layer order list with the layer's index. */ |
517 | 0 | map->layerorder[l] = l; |
518 | 0 | } |
519 | | |
520 | 0 | GET_LAYER(map, l)->status = MS_ON; |
521 | 0 | if (map->scalebar.postlabelcache) { /* add it directly to the image */ |
522 | 0 | if (msMaybeAllocateClassStyle(GET_LAYER(map, l)->class[0], 0) == MS_FAILURE) |
523 | 0 | return MS_FAILURE; |
524 | 0 | GET_LAYER(map, l)->class[0]->styles[0]->symbol = s; |
525 | 0 | status = msDrawMarkerSymbol(map, img, &point, |
526 | 0 | GET_LAYER(map, l)->class[0] -> styles[0], 1.0); |
527 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
528 | 0 | goto embed_cleanup; |
529 | 0 | } |
530 | 0 | } else { |
531 | 0 | if (!GET_LAYER(map, l)->class[0] -> labels) { |
532 | 0 | if (msGrowClassLabels(GET_LAYER(map, l)->class[0]) == NULL) |
533 | 0 | return MS_FAILURE; |
534 | 0 | initLabel(GET_LAYER(map, l)->class[0] -> labels[0]); |
535 | 0 | GET_LAYER(map, l)->class[0]->numlabels = 1; |
536 | 0 | GET_LAYER(map, l)->class[0]->labels[0]->force = MS_TRUE; |
537 | 0 | GET_LAYER(map, l)->class[0]->labels[0]->size = |
538 | 0 | MS_MEDIUM; /* must set a size to have a valid label definition */ |
539 | 0 | GET_LAYER(map, l)->class[0]->labels[0]->priority = MS_MAX_LABEL_PRIORITY; |
540 | 0 | } |
541 | 0 | if (GET_LAYER(map, l)->class[0] -> labels[0] -> numstyles == 0) { |
542 | 0 | if (msGrowLabelStyles(GET_LAYER(map, l)->class[0] -> labels[0]) == NULL) |
543 | 0 | return (MS_FAILURE); |
544 | 0 | GET_LAYER(map, l)->class[0]->labels[0]->numstyles = 1; |
545 | 0 | initStyle(GET_LAYER(map, l)->class[0] -> labels[0] -> styles[0]); |
546 | 0 | GET_LAYER(map, l)->class[0]->labels[0]->styles[0]->_geomtransform.type = |
547 | 0 | MS_GEOMTRANSFORM_LABELPOINT; |
548 | 0 | } |
549 | 0 | GET_LAYER(map, l)->class[0]->labels[0]->styles[0]->symbol = s; |
550 | 0 | status = msAddLabel(map, img, GET_LAYER(map, l)->class[0] -> labels[0], l, |
551 | 0 | 0, NULL, &point, -1, NULL); |
552 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) { |
553 | 0 | goto embed_cleanup; |
554 | 0 | } |
555 | 0 | } |
556 | | |
557 | 0 | embed_cleanup: |
558 | | /* Mark layer as deleted so that it doesn't interfere with html legends or |
559 | | * with saving maps */ |
560 | 0 | GET_LAYER(map, l)->status = MS_DELETE; |
561 | |
|
562 | 0 | msFreeImage(image); |
563 | 0 | return status; |
564 | 0 | } |
565 | | |
566 | | /************************************************************************/ |
567 | | /* These two functions are used in PHP/Mapscript and Swig/Mapscript */ |
568 | | /************************************************************************/ |
569 | | |
570 | | /************************************************************************/ |
571 | | /* double GetDeltaExtentsUsingScale(double scale, int units, */ |
572 | | /* double centerLat, int width, */ |
573 | | /* double resolution) */ |
574 | | /* */ |
575 | | /* Utility function to return the maximum extent using the */ |
576 | | /* scale and the width of the image. */ |
577 | | /* */ |
578 | | /* Base on the function msCalculateScale (mapscale.c) */ |
579 | | /************************************************************************/ |
580 | | double GetDeltaExtentsUsingScale(double scale, int units, double centerLat, |
581 | 0 | int width, double resolution) { |
582 | 0 | double md = 0.0; |
583 | 0 | double dfDelta = -1.0; |
584 | |
|
585 | 0 | if (scale <= 0 || width <= 0) |
586 | 0 | return -1; |
587 | | |
588 | 0 | switch (units) { |
589 | 0 | case (MS_DD): |
590 | 0 | case (MS_METERS): |
591 | 0 | case (MS_KILOMETERS): |
592 | 0 | case (MS_MILES): |
593 | 0 | case (MS_NAUTICALMILES): |
594 | 0 | case (MS_INCHES): |
595 | 0 | case (MS_FEET): |
596 | | /* remember, we use a pixel-center to pixel-center extent, hence the width-1 |
597 | | */ |
598 | 0 | md = (width - 1) / (resolution * msInchesPerUnit(units, centerLat)); |
599 | 0 | dfDelta = md * scale; |
600 | 0 | break; |
601 | | |
602 | 0 | default: |
603 | 0 | break; |
604 | 0 | } |
605 | | |
606 | 0 | return dfDelta; |
607 | 0 | } |
608 | | |
609 | | /************************************************************************/ |
610 | | /* static double Pix2Georef(int nPixPos, int nPixMin, double nPixMax,*/ |
611 | | /* double dfGeoMin, double dfGeoMax, */ |
612 | | /* bool bULisYOrig) */ |
613 | | /* */ |
614 | | /* Utility function to convert a pixel pos to georef pos. If */ |
615 | | /* bULisYOrig parameter is set to true then the upper left is */ |
616 | | /* considered to be the Y origin. */ |
617 | | /* */ |
618 | | /************************************************************************/ |
619 | | double Pix2Georef(int nPixPos, int nPixMin, int nPixMax, double dfGeoMin, |
620 | 0 | double dfGeoMax, int bULisYOrig) { |
621 | 0 | double dfPosGeo = 0.0; |
622 | |
|
623 | 0 | const double dfWidthGeo = dfGeoMax - dfGeoMin; |
624 | 0 | const int nWidthPix = nPixMax - nPixMin; |
625 | |
|
626 | 0 | if (dfWidthGeo > 0.0 && nWidthPix > 0) { |
627 | 0 | const double dfPixToGeo = dfWidthGeo / (double)nWidthPix; |
628 | |
|
629 | 0 | int nDeltaPix; |
630 | 0 | if (!bULisYOrig) |
631 | 0 | nDeltaPix = nPixPos - nPixMin; |
632 | 0 | else |
633 | 0 | nDeltaPix = nPixMax - nPixPos; |
634 | |
|
635 | 0 | const double dfDeltaGeo = nDeltaPix * dfPixToGeo; |
636 | |
|
637 | 0 | dfPosGeo = dfGeoMin + dfDeltaGeo; |
638 | 0 | } |
639 | 0 | return (dfPosGeo); |
640 | 0 | } |
641 | | |
642 | | /* This function converts a pixel value in geo ref. The return value is in |
643 | | * layer units. This function has been added for the purpose of the ticket #1340 |
644 | | */ |
645 | | |
646 | 0 | double Pix2LayerGeoref(mapObj *map, layerObj *layer, int value) { |
647 | 0 | double cellsize = |
648 | 0 | MS_MAX(MS_CELLSIZE(map->extent.minx, map->extent.maxx, map->width), |
649 | 0 | MS_CELLSIZE(map->extent.miny, map->extent.maxy, map->height)); |
650 | |
|
651 | 0 | double resolutionFactor = map->resolution / map->defresolution; |
652 | 0 | double unitsFactor = 1; |
653 | |
|
654 | 0 | if (layer->sizeunits != MS_PIXELS) |
655 | 0 | unitsFactor = |
656 | 0 | msInchesPerUnit(map->units, 0) / msInchesPerUnit(layer->sizeunits, 0); |
657 | |
|
658 | 0 | return value * cellsize * resolutionFactor * unitsFactor; |
659 | 0 | } |