/src/MapServer/src/maputfgrid.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: UTFGrid rendering functions (using AGG) |
6 | | * Author: Francois Desjarlais |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 1996-2007 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 | | #include "maputfgrid.h" |
32 | | #include "mapagg.h" |
33 | | #include "renderers/agg/include/agg_rasterizer_scanline_aa.h" |
34 | | #include "renderers/agg/include/agg_basics.h" |
35 | | #include "renderers/agg/include/agg_renderer_scanline.h" |
36 | | #include "renderers/agg/include/agg_scanline_bin.h" |
37 | | #include "renderers/agg/include/agg_gamma_functions.h" |
38 | | #include "renderers/agg/include/agg_conv_stroke.h" |
39 | | #include "renderers/agg/include/agg_ellipse.h" |
40 | | |
41 | | #include <memory> |
42 | | #include <vector> |
43 | | |
44 | | typedef mapserver::int32u band_type; |
45 | | typedef mapserver::row_ptr_cache<band_type> rendering_buffer; |
46 | | typedef pixfmt_utf<utfpix32, rendering_buffer> pixfmt_utf32; |
47 | | typedef mapserver::renderer_base<pixfmt_utf32> renderer_base; |
48 | | typedef mapserver::rasterizer_scanline_aa<> rasterizer_scanline; |
49 | | typedef mapserver::renderer_scanline_bin_solid<renderer_base> renderer_scanline; |
50 | | |
51 | | static const utfpix32 UTF_WATER = utfpix32(32); |
52 | | |
53 | 0 | #define utfitem(c) utfpix32(c) |
54 | | |
55 | | namespace { |
56 | | |
57 | | struct shapeData { |
58 | | std::string datavalues{}; |
59 | | std::string itemvalue{}; |
60 | | band_type utfvalue = 0; |
61 | | int serialid = 0; |
62 | | }; |
63 | | |
64 | | /* |
65 | | * UTFGrid specific polygon adaptor. |
66 | | */ |
67 | | class polygon_adaptor_utf final : public polygon_adaptor { |
68 | | public: |
69 | | polygon_adaptor_utf(shapeObj *shape, int utfres_in) |
70 | 0 | : polygon_adaptor(shape), utfresolution(utfres_in) {} |
71 | | |
72 | 0 | unsigned vertex(double *x, double *y) override { |
73 | 0 | if (m_point < m_pend) { |
74 | 0 | bool first = m_point == m_line->point; |
75 | 0 | *x = m_point->x / utfresolution; |
76 | 0 | *y = m_point->y / utfresolution; |
77 | 0 | m_point++; |
78 | 0 | return first ? mapserver::path_cmd_move_to : mapserver::path_cmd_line_to; |
79 | 0 | } |
80 | 0 | *x = *y = 0.0; |
81 | 0 | if (!m_stop) { |
82 | |
|
83 | 0 | m_line++; |
84 | 0 | if (m_line >= m_lend) { |
85 | 0 | m_stop = true; |
86 | 0 | return mapserver::path_cmd_end_poly; |
87 | 0 | } |
88 | | |
89 | 0 | m_point = m_line->point; |
90 | 0 | m_pend = &(m_line->point[m_line->numpoints]); |
91 | 0 | return mapserver::path_cmd_end_poly; |
92 | 0 | } |
93 | 0 | return mapserver::path_cmd_stop; |
94 | 0 | } |
95 | | |
96 | | private: |
97 | | int utfresolution; |
98 | | }; |
99 | | |
100 | | /* |
101 | | * UTFGrid specific line adaptor. |
102 | | */ |
103 | | class line_adaptor_utf final : public line_adaptor { |
104 | | public: |
105 | | line_adaptor_utf(shapeObj *shape, int utfres_in) |
106 | 0 | : line_adaptor(shape), utfresolution(utfres_in) {} |
107 | | |
108 | 0 | unsigned vertex(double *x, double *y) override { |
109 | 0 | if (m_point < m_pend) { |
110 | 0 | bool first = m_point == m_line->point; |
111 | 0 | *x = m_point->x / utfresolution; |
112 | 0 | *y = m_point->y / utfresolution; |
113 | 0 | m_point++; |
114 | 0 | return first ? mapserver::path_cmd_move_to : mapserver::path_cmd_line_to; |
115 | 0 | } |
116 | 0 | m_line++; |
117 | 0 | *x = *y = 0.0; |
118 | 0 | if (m_line >= m_lend) |
119 | 0 | return mapserver::path_cmd_stop; |
120 | | |
121 | 0 | m_point = m_line->point; |
122 | 0 | m_pend = &(m_line->point[m_line->numpoints]); |
123 | |
|
124 | 0 | return vertex(x, y); |
125 | 0 | } |
126 | | |
127 | | private: |
128 | | int utfresolution; |
129 | | }; |
130 | | |
131 | | class UTFGridRenderer { |
132 | | public: |
133 | | std::vector<shapeData> table{}; |
134 | | int utfresolution = 0; |
135 | | int layerwatch = 0; |
136 | | bool renderlayer = false; |
137 | | bool useutfitem = false; |
138 | | bool useutfdata = false; |
139 | | bool duplicates = false; |
140 | | band_type utfvalue = 0; |
141 | | layerObj *utflayer = nullptr; |
142 | | int width = 0; |
143 | | int height = 0; |
144 | | std::vector<band_type> buffer{}; |
145 | | rendering_buffer m_rendering_buffer{}; |
146 | | pixfmt_utf32 m_pixel_format{}; |
147 | | renderer_base m_renderer_base{}; |
148 | | rasterizer_scanline m_rasterizer{}; |
149 | | renderer_scanline m_renderer_scanline{}; |
150 | | mapserver::scanline_bin sl_utf{}; |
151 | | std::unique_ptr<mapserver::conv_stroke<line_adaptor_utf>> stroke{}; |
152 | | }; |
153 | | |
154 | | } // namespace |
155 | | |
156 | | #define UTFGRID_RENDERER(image) \ |
157 | 0 | (static_cast<UTFGridRenderer *>((image)->img.plugin)) |
158 | | |
159 | | /* |
160 | | * Encode to avoid unavailable char in the JSON |
161 | | */ |
162 | 0 | static unsigned int encodeForRendering(unsigned int toencode) { |
163 | 0 | unsigned int encoded; |
164 | 0 | encoded = toencode + 32; |
165 | | /* 34 => " */ |
166 | 0 | if (encoded >= 34) { |
167 | 0 | encoded = encoded + 1; |
168 | 0 | } |
169 | | /* 92 => \ */ |
170 | 0 | if (encoded >= 92) { |
171 | 0 | encoded = encoded + 1; |
172 | 0 | } |
173 | 0 | return encoded; |
174 | 0 | } |
175 | | |
176 | | /* |
177 | | * Decode value to have the initial one |
178 | | */ |
179 | 0 | static unsigned int decodeRendered(unsigned int todecode) { |
180 | 0 | unsigned int decoded; |
181 | |
|
182 | 0 | if (todecode >= 92) |
183 | 0 | todecode--; |
184 | |
|
185 | 0 | if (todecode >= 34) |
186 | 0 | todecode--; |
187 | |
|
188 | 0 | decoded = todecode - 32; |
189 | |
|
190 | 0 | return decoded; |
191 | 0 | } |
192 | | |
193 | | /* |
194 | | * Add the shapeObj UTFDATA and UTFITEM to the lookup table. |
195 | | */ |
196 | 0 | static band_type addToTable(UTFGridRenderer *r, shapeObj *p) { |
197 | | /* Looks for duplicates. */ |
198 | 0 | if (r->duplicates == false && r->useutfitem) { |
199 | 0 | const auto itemvalue = p->values[r->utflayer->utfitemindex]; |
200 | 0 | for (const auto &tableItem : r->table) { |
201 | 0 | if (tableItem.itemvalue == itemvalue) { |
202 | | /* Found a copy of the values in the table. */ |
203 | 0 | return tableItem.utfvalue; |
204 | 0 | } |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | /* Data added to the table */ |
209 | 0 | shapeData table; |
210 | 0 | char *escaped = msEvalTextExpressionJSonEscape(&r->utflayer->utfdata, p); |
211 | 0 | table.datavalues = escaped ? escaped : ""; |
212 | 0 | msFree(escaped); |
213 | | |
214 | | /* If UTFITEM is set in the mapfile we add its value to the table */ |
215 | 0 | if (r->useutfitem) |
216 | 0 | table.itemvalue = p->values[r->utflayer->utfitemindex]; |
217 | |
|
218 | 0 | table.serialid = static_cast<int>(r->table.size() + 1); |
219 | | |
220 | | /* Simple operation so we don't have unavailable char in the JSON */ |
221 | 0 | const auto utfvalue = encodeForRendering(table.serialid); |
222 | 0 | table.utfvalue = utfvalue; |
223 | |
|
224 | 0 | r->table.emplace_back(std::move(table)); |
225 | |
|
226 | 0 | return utfvalue; |
227 | 0 | } |
228 | | |
229 | | /* |
230 | | * Use AGG to render any path. |
231 | | */ |
232 | | template <class vertex_source> |
233 | 0 | static int utfgridRenderPath(imageObj *img, vertex_source &path) { |
234 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
235 | 0 | r->m_rasterizer.reset(); |
236 | 0 | r->m_rasterizer.filling_rule(mapserver::fill_even_odd); |
237 | 0 | r->m_rasterizer.add_path(path); |
238 | 0 | r->m_renderer_scanline.color(utfitem(r->utfvalue)); |
239 | 0 | mapserver::render_scanlines(r->m_rasterizer, r->sl_utf, |
240 | 0 | r->m_renderer_scanline); |
241 | 0 | return MS_SUCCESS; |
242 | 0 | } Unexecuted instantiation: maputfgrid.cpp:int utfgridRenderPath<(anonymous namespace)::polygon_adaptor_utf>(imageObj*, (anonymous namespace)::polygon_adaptor_utf&) Unexecuted instantiation: maputfgrid.cpp:int utfgridRenderPath<mapserver::path_base<mapserver::vertex_block_storage<double, 8u, 256u> > >(imageObj*, mapserver::path_base<mapserver::vertex_block_storage<double, 8u, 256u> >&) Unexecuted instantiation: maputfgrid.cpp:int utfgridRenderPath<mapserver::conv_stroke<(anonymous namespace)::line_adaptor_utf, mapserver::null_markers> >(imageObj*, mapserver::conv_stroke<(anonymous namespace)::line_adaptor_utf, mapserver::null_markers>&) |
243 | | |
244 | | /* |
245 | | * Initialize the renderer, create buffer, allocate memory. |
246 | | */ |
247 | | static imageObj *utfgridCreateImage(int width, int height, |
248 | | outputFormatObj *format, |
249 | 0 | colorObj * /*bg*/) { |
250 | 0 | auto r = std::unique_ptr<UTFGridRenderer>(new UTFGridRenderer); |
251 | |
|
252 | 0 | r->utfresolution = |
253 | 0 | atof(msGetOutputFormatOption(format, "UTFRESOLUTION", "4")); |
254 | 0 | if (r->utfresolution < 1) { |
255 | 0 | msSetError(MS_MISCERR, "UTFRESOLUTION smaller that 1 in the mapfile.", |
256 | 0 | "utfgridCreateImage()"); |
257 | 0 | return nullptr; |
258 | 0 | } |
259 | 0 | r->duplicates = |
260 | 0 | EQUAL("true", msGetOutputFormatOption(format, "DUPLICATES", "true")); |
261 | |
|
262 | 0 | r->width = width / r->utfresolution; |
263 | 0 | r->height = height / r->utfresolution; |
264 | |
|
265 | 0 | r->buffer.resize(static_cast<size_t>(r->width) * r->height); |
266 | | |
267 | | /* AGG specific operations */ |
268 | 0 | r->m_rendering_buffer.attach(&r->buffer[0], r->width, r->height, r->width); |
269 | 0 | r->m_pixel_format.attach(r->m_rendering_buffer); |
270 | 0 | r->m_renderer_base.attach(r->m_pixel_format); |
271 | 0 | r->m_renderer_scanline.attach(r->m_renderer_base); |
272 | 0 | r->m_renderer_base.clear(UTF_WATER); |
273 | 0 | r->m_rasterizer.gamma(mapserver::gamma_none()); |
274 | |
|
275 | 0 | imageObj *image = (imageObj *)msSmallCalloc(1, sizeof(imageObj)); |
276 | 0 | image->img.plugin = r.release(); |
277 | |
|
278 | 0 | return image; |
279 | 0 | } |
280 | | |
281 | | /* |
282 | | * Free all the memory used by the renderer. |
283 | | */ |
284 | 0 | static int utfgridFreeImage(imageObj *img) { |
285 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
286 | 0 | delete r; |
287 | |
|
288 | 0 | img->img.plugin = NULL; |
289 | |
|
290 | 0 | return MS_SUCCESS; |
291 | 0 | } |
292 | | |
293 | | /* |
294 | | * Update a character in the utfgrid. |
295 | | */ |
296 | | static int utfgridUpdateChar(imageObj *img, band_type oldChar, |
297 | 0 | band_type newChar) { |
298 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
299 | |
|
300 | 0 | for (auto &ch : r->buffer) { |
301 | 0 | if (ch == oldChar) { |
302 | 0 | ch = newChar; |
303 | 0 | } |
304 | 0 | } |
305 | |
|
306 | 0 | return MS_SUCCESS; |
307 | 0 | } |
308 | | |
309 | | /* |
310 | | * Remove unnecessary data that didn't made it to the final grid. |
311 | | */ |
312 | 0 | static int utfgridCleanData(imageObj *img) { |
313 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
314 | |
|
315 | 0 | std::vector<bool> usedChar; |
316 | 0 | usedChar.resize(r->table.size()); |
317 | |
|
318 | 0 | int itemFound = 0; |
319 | |
|
320 | 0 | for (const auto ch : r->buffer) { |
321 | 0 | const auto rendered = decodeRendered(ch); |
322 | 0 | if (rendered != 0 && !usedChar[rendered - 1]) { |
323 | 0 | itemFound++; |
324 | 0 | usedChar[rendered - 1] = true; |
325 | 0 | } |
326 | 0 | } |
327 | |
|
328 | 0 | std::vector<shapeData> updatedData; |
329 | 0 | updatedData.reserve(itemFound); |
330 | |
|
331 | 0 | for (const auto &tableItem : r->table) { |
332 | 0 | if (usedChar[decodeRendered(tableItem.utfvalue) - 1]) { |
333 | 0 | updatedData.emplace_back(tableItem); |
334 | 0 | auto &newData = updatedData.back(); |
335 | |
|
336 | 0 | newData.serialid = static_cast<int>(updatedData.size()); |
337 | |
|
338 | 0 | const band_type utfvalue = encodeForRendering(newData.serialid); |
339 | |
|
340 | 0 | utfgridUpdateChar(img, newData.utfvalue, utfvalue); |
341 | 0 | newData.utfvalue = utfvalue; |
342 | 0 | } |
343 | 0 | } |
344 | |
|
345 | 0 | r->table = std::move(updatedData); |
346 | |
|
347 | 0 | return MS_SUCCESS; |
348 | 0 | } |
349 | | |
350 | | /* |
351 | | * Print the renderer data as JSON. |
352 | | */ |
353 | | static int utfgridSaveImage(imageObj *img, mapObj * /*map*/, FILE *fp, |
354 | 0 | outputFormatObj * /*format*/) { |
355 | 0 | utfgridCleanData(img); |
356 | |
|
357 | 0 | const UTFGridRenderer *renderer = UTFGRID_RENDERER(img); |
358 | |
|
359 | 0 | if (renderer->layerwatch > 1) |
360 | 0 | return MS_FAILURE; |
361 | | |
362 | 0 | const int imgheight = renderer->height; |
363 | 0 | const int imgwidth = renderer->width; |
364 | |
|
365 | 0 | msIO_fprintf(fp, "{\"grid\":["); |
366 | | |
367 | | /* Print the buffer */ |
368 | 0 | std::vector<wchar_t> string; |
369 | 0 | string.resize(imgwidth + 1); |
370 | 0 | for (int row = 0; row < imgheight; row++) { |
371 | | |
372 | | /* Need a comma between each line but JSON must not start with a comma. */ |
373 | 0 | if (row != 0) |
374 | 0 | msIO_fprintf(fp, ","); |
375 | 0 | msIO_fprintf(fp, "\""); |
376 | 0 | for (int col = 0; col < imgwidth; col++) { |
377 | | /* Get the data from buffer. */ |
378 | 0 | band_type pixelid = renderer->buffer[(row * imgwidth) + col]; |
379 | 0 | string[col] = pixelid; |
380 | 0 | } |
381 | | |
382 | | /* Conversion to UTF-8 encoding */ |
383 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
384 | | const char *encoding = "UCS-2LE"; |
385 | | #else |
386 | 0 | const char *encoding = "UCS-4LE"; |
387 | 0 | #endif |
388 | |
|
389 | 0 | char *utf8 = msConvertWideStringToUTF8(string.data(), encoding); |
390 | 0 | msIO_fprintf(fp, "%s", utf8); |
391 | 0 | msFree(utf8); |
392 | 0 | msIO_fprintf(fp, "\""); |
393 | 0 | } |
394 | |
|
395 | 0 | msIO_fprintf(fp, "],\"keys\":[\"\""); |
396 | | |
397 | | /* Print the specified key */ |
398 | 0 | for (const auto &table : renderer->table) { |
399 | 0 | msIO_fprintf(fp, ","); |
400 | |
|
401 | 0 | if (renderer->useutfitem) { |
402 | 0 | char *pszEscaped = msEscapeJSonString(table.itemvalue.c_str()); |
403 | 0 | msIO_fprintf(fp, "\"%s\"", pszEscaped); |
404 | 0 | msFree(pszEscaped); |
405 | 0 | } |
406 | | /* If no UTFITEM specified use the serial ID as the key */ |
407 | 0 | else |
408 | 0 | msIO_fprintf(fp, "\"%i\"", table.serialid); |
409 | 0 | } |
410 | |
|
411 | 0 | msIO_fprintf(fp, "],\"data\":{"); |
412 | | |
413 | | /* Print the data */ |
414 | 0 | if (renderer->useutfdata) { |
415 | 0 | bool first = true; |
416 | 0 | for (const auto &table : renderer->table) { |
417 | 0 | if (!first) |
418 | 0 | msIO_fprintf(fp, ","); |
419 | 0 | first = false; |
420 | |
|
421 | 0 | if (renderer->useutfitem) { |
422 | 0 | char *pszEscaped = msEscapeJSonString(table.itemvalue.c_str()); |
423 | 0 | msIO_fprintf(fp, "\"%s\":", pszEscaped); |
424 | 0 | msFree(pszEscaped); |
425 | 0 | } |
426 | | /* If no UTFITEM specified use the serial ID as the key */ |
427 | 0 | else |
428 | 0 | msIO_fprintf(fp, "\"%i\":", table.serialid); |
429 | |
|
430 | 0 | msIO_fprintf(fp, "%s", table.datavalues.c_str()); |
431 | 0 | } |
432 | 0 | } |
433 | 0 | msIO_fprintf(fp, "}}"); |
434 | |
|
435 | 0 | return MS_SUCCESS; |
436 | 0 | } |
437 | | |
438 | | /* |
439 | | * Starts a layer for UTFGrid renderer. |
440 | | */ |
441 | 0 | static int utfgridStartLayer(imageObj *img, mapObj * /*map*/, layerObj *layer) { |
442 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
443 | | |
444 | | /* Look if the layer uses the UTFGrid output format */ |
445 | 0 | if (layer->utfdata.string != 0) { |
446 | 0 | r->useutfdata = true; |
447 | 0 | } |
448 | | |
449 | | /* layerwatch is set to 1 on first layer treated. Doesn't allow multiple |
450 | | * layers. */ |
451 | 0 | if (!r->layerwatch) { |
452 | 0 | r->layerwatch++; |
453 | |
|
454 | 0 | r->renderlayer = true; |
455 | 0 | r->utflayer = layer; |
456 | 0 | layer->refcount++; |
457 | | |
458 | | /* Verify if renderer needs to use UTFITEM */ |
459 | 0 | if (r->utflayer->utfitem) |
460 | 0 | r->useutfitem = true; |
461 | 0 | } |
462 | | /* If multiple layers, send error */ |
463 | 0 | else { |
464 | 0 | r->layerwatch++; |
465 | 0 | msSetError(MS_MISCERR, |
466 | 0 | "MapServer does not support multiple UTFGrid layers rendering " |
467 | 0 | "simultaneously.", |
468 | 0 | "utfgridStartLayer()"); |
469 | 0 | return MS_FAILURE; |
470 | 0 | } |
471 | | |
472 | 0 | return MS_SUCCESS; |
473 | 0 | } |
474 | | |
475 | | /* |
476 | | * Tell renderer the layer is done. |
477 | | */ |
478 | 0 | static int utfgridEndLayer(imageObj *img, mapObj * /*map*/, layerObj *layer) { |
479 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
480 | | |
481 | | /* Look if the layer was rendered, if it was then turn off rendering. */ |
482 | 0 | if (r->renderlayer) { |
483 | 0 | r->utflayer = NULL; |
484 | 0 | layer->refcount--; |
485 | 0 | r->renderlayer = false; |
486 | 0 | } |
487 | |
|
488 | 0 | return MS_SUCCESS; |
489 | 0 | } |
490 | | |
491 | | /* |
492 | | * Do the table operations on the shapes. Allow multiple types of data to be |
493 | | * rendered. |
494 | | */ |
495 | 0 | static int utfgridStartShape(imageObj *img, shapeObj *shape) { |
496 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
497 | |
|
498 | 0 | if (!r->renderlayer) |
499 | 0 | return MS_FAILURE; |
500 | | |
501 | | /* Table operations */ |
502 | 0 | r->utfvalue = addToTable(r, shape); |
503 | |
|
504 | 0 | return MS_SUCCESS; |
505 | 0 | } |
506 | | |
507 | | /* |
508 | | * Tells the renderer that the shape's rendering is done. |
509 | | */ |
510 | 0 | static int utfgridEndShape(imageObj *img, shapeObj *) { |
511 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
512 | |
|
513 | 0 | r->utfvalue = 0; |
514 | 0 | return MS_SUCCESS; |
515 | 0 | } |
516 | | |
517 | | /* |
518 | | * Function that renders polygons into UTFGrid. |
519 | | */ |
520 | | static int utfgridRenderPolygon(imageObj *img, shapeObj *polygonshape, |
521 | 0 | colorObj *) { |
522 | 0 | const UTFGridRenderer *r = UTFGRID_RENDERER(img); |
523 | | |
524 | | /* utfvalue is set to 0 if the shape isn't in the table. */ |
525 | 0 | if (r->utfvalue == 0) { |
526 | 0 | return MS_FAILURE; |
527 | 0 | } |
528 | | |
529 | | /* Render the polygon */ |
530 | 0 | polygon_adaptor_utf polygons(polygonshape, r->utfresolution); |
531 | 0 | utfgridRenderPath(img, polygons); |
532 | |
|
533 | 0 | return MS_SUCCESS; |
534 | 0 | } |
535 | | |
536 | | /* |
537 | | * Function that renders lines into UTFGrid. Starts by looking if the line is a |
538 | | * polygon outline, draw it if it's not. |
539 | | */ |
540 | | static int utfgridRenderLine(imageObj *img, shapeObj *lineshape, |
541 | 0 | strokeStyleObj *linestyle) { |
542 | 0 | UTFGridRenderer *r = UTFGRID_RENDERER(img); |
543 | | |
544 | | /* utfvalue is set to 0 if the shape isn't in the table. */ |
545 | 0 | if (r->utfvalue == 0) { |
546 | 0 | return MS_SUCCESS; |
547 | | /* If we don't get a character to draw we just skip execution |
548 | | * instead of failing |
549 | | */ |
550 | 0 | } |
551 | | |
552 | | /* Render the line */ |
553 | 0 | line_adaptor_utf lines(lineshape, r->utfresolution); |
554 | |
|
555 | 0 | if (!r->stroke) { |
556 | 0 | r->stroke.reset(new mapserver::conv_stroke<line_adaptor_utf>(lines)); |
557 | 0 | } else { |
558 | 0 | r->stroke->attach(lines); |
559 | 0 | } |
560 | 0 | r->stroke->width(linestyle->width / r->utfresolution); |
561 | 0 | utfgridRenderPath(img, *r->stroke); |
562 | |
|
563 | 0 | return MS_SUCCESS; |
564 | 0 | } |
565 | | |
566 | | /* |
567 | | * Function that render vector type symbols into UTFGrid. |
568 | | */ |
569 | | static int utfgridRenderVectorSymbol(imageObj *img, double x, double y, |
570 | 0 | symbolObj *symbol, symbolStyleObj *style) { |
571 | 0 | const UTFGridRenderer *r = UTFGRID_RENDERER(img); |
572 | 0 | double ox = symbol->sizex * 0.5; |
573 | 0 | double oy = symbol->sizey * 0.5; |
574 | | |
575 | | /* utfvalue is set to 0 if the shape isn't in the table. */ |
576 | 0 | if (r->utfvalue == 0) { |
577 | 0 | return MS_FAILURE; |
578 | 0 | } |
579 | | |
580 | | /* Pathing the symbol */ |
581 | 0 | mapserver::path_storage path = imageVectorSymbol(symbol); |
582 | | |
583 | | /* Transformation to the right size/scale. */ |
584 | 0 | mapserver::trans_affine mtx; |
585 | 0 | mtx *= mapserver::trans_affine_translation(-ox, -oy); |
586 | 0 | mtx *= mapserver::trans_affine_scaling(style->scale / r->utfresolution); |
587 | 0 | mtx *= mapserver::trans_affine_rotation(-style->rotation); |
588 | 0 | mtx *= mapserver::trans_affine_translation(x / r->utfresolution, |
589 | 0 | y / r->utfresolution); |
590 | 0 | path.transform(mtx); |
591 | | |
592 | | /* Rendering the symbol. */ |
593 | 0 | utfgridRenderPath(img, path); |
594 | |
|
595 | 0 | return MS_SUCCESS; |
596 | 0 | } |
597 | | |
598 | | /* |
599 | | * Function that renders Pixmap type symbols into UTFGrid. |
600 | | */ |
601 | | static int utfgridRenderPixmapSymbol(imageObj *img, double x, double y, |
602 | 0 | symbolObj *symbol, symbolStyleObj *style) { |
603 | 0 | const UTFGridRenderer *r = UTFGRID_RENDERER(img); |
604 | 0 | rasterBufferObj *pixmap = symbol->pixmap_buffer; |
605 | | |
606 | | /* utfvalue is set to 0 if the shape isn't in the table. */ |
607 | 0 | if (r->utfvalue == 0) { |
608 | 0 | return MS_FAILURE; |
609 | 0 | } |
610 | | |
611 | | /* Pathing the symbol BBox */ |
612 | 0 | mapserver::path_storage pixmap_bbox; |
613 | 0 | double w, h; |
614 | 0 | w = pixmap->width * style->scale / (2.0); |
615 | 0 | h = pixmap->height * style->scale / (2.0); |
616 | 0 | pixmap_bbox.move_to((x - w) / r->utfresolution, (y - h) / r->utfresolution); |
617 | 0 | pixmap_bbox.line_to((x + w) / r->utfresolution, (y - h) / r->utfresolution); |
618 | 0 | pixmap_bbox.line_to((x + w) / r->utfresolution, (y + h) / r->utfresolution); |
619 | 0 | pixmap_bbox.line_to((x - w) / r->utfresolution, (y + h) / r->utfresolution); |
620 | | |
621 | | /* Rendering the symbol */ |
622 | 0 | utfgridRenderPath(img, pixmap_bbox); |
623 | |
|
624 | 0 | return MS_SUCCESS; |
625 | 0 | } |
626 | | |
627 | | /* |
628 | | * Function that render ellipse type symbols into UTFGrid. |
629 | | */ |
630 | | static int utfgridRenderEllipseSymbol(imageObj *img, double x, double y, |
631 | | symbolObj *symbol, |
632 | 0 | symbolStyleObj *style) { |
633 | 0 | const UTFGridRenderer *r = UTFGRID_RENDERER(img); |
634 | | |
635 | | /* utfvalue is set to 0 if the shape isn't in the table. */ |
636 | 0 | if (r->utfvalue == 0) { |
637 | 0 | return MS_FAILURE; |
638 | 0 | } |
639 | | |
640 | | /* Pathing the symbol. */ |
641 | 0 | mapserver::path_storage path; |
642 | 0 | mapserver::ellipse ellipse( |
643 | 0 | x / r->utfresolution, y / r->utfresolution, |
644 | 0 | symbol->sizex * style->scale / 2 / r->utfresolution, |
645 | 0 | symbol->sizey * style->scale / 2 / r->utfresolution); |
646 | 0 | path.concat_path(ellipse); |
647 | | |
648 | | /* Rotation if necessary. */ |
649 | 0 | if (style->rotation != 0) { |
650 | 0 | mapserver::trans_affine mtx; |
651 | 0 | mtx *= mapserver::trans_affine_translation(-x / r->utfresolution, |
652 | 0 | -y / r->utfresolution); |
653 | 0 | mtx *= mapserver::trans_affine_rotation(-style->rotation); |
654 | 0 | mtx *= mapserver::trans_affine_translation(x / r->utfresolution, |
655 | 0 | y / r->utfresolution); |
656 | 0 | path.transform(mtx); |
657 | 0 | } |
658 | | |
659 | | /* Rendering the symbol. */ |
660 | 0 | utfgridRenderPath(img, path); |
661 | |
|
662 | 0 | return MS_SUCCESS; |
663 | 0 | } |
664 | | |
665 | | static int utfgridRenderGlyphs(imageObj *img, const textSymbolObj *ts, |
666 | | colorObj * /*c*/, colorObj * /*oc*/, int /*ow*/, |
667 | 0 | int isMarker) { |
668 | |
|
669 | 0 | const textPathObj *tp = ts->textpath; |
670 | 0 | const UTFGridRenderer *r = UTFGRID_RENDERER(img); |
671 | | |
672 | | /* If it's not a marker then it's a label or other thing and we don't |
673 | | * want to draw it on the map |
674 | | */ |
675 | 0 | if (!isMarker) { |
676 | 0 | return MS_SUCCESS; // Stop the rendering with no errors |
677 | 0 | } |
678 | | |
679 | | /* utfvalue is set to 0 if the shape isn't in the table. */ |
680 | 0 | if (r->utfvalue == 0) { |
681 | 0 | return MS_SUCCESS; // Stop the rendering with no errors |
682 | 0 | } |
683 | | |
684 | | /* Pathing the symbol BBox */ |
685 | 0 | mapserver::path_storage box; |
686 | 0 | double size, x, y; |
687 | |
|
688 | 0 | size = tp->glyph_size; |
689 | 0 | ; |
690 | 0 | x = tp->glyphs->pnt.x; |
691 | 0 | y = tp->glyphs->pnt.y; |
692 | |
|
693 | 0 | box.move_to((x) / r->utfresolution, (y) / r->utfresolution); |
694 | 0 | box.line_to((x + size) / r->utfresolution, (y) / r->utfresolution); |
695 | 0 | box.line_to((x + size) / r->utfresolution, (y - size) / r->utfresolution); |
696 | 0 | box.line_to((x) / r->utfresolution, (y - size) / r->utfresolution); |
697 | | |
698 | | /* Rotation if necessary. */ |
699 | 0 | if (tp->glyphs->rot != 0) { |
700 | 0 | mapserver::trans_affine mtx; |
701 | 0 | mtx *= mapserver::trans_affine_translation(-x / r->utfresolution, |
702 | 0 | -y / r->utfresolution); |
703 | 0 | mtx *= mapserver::trans_affine_rotation(-tp->glyphs->rot); |
704 | 0 | mtx *= mapserver::trans_affine_translation(x / r->utfresolution, |
705 | 0 | y / r->utfresolution); |
706 | 0 | box.transform(mtx); |
707 | 0 | } |
708 | | |
709 | | /* Rendering the symbol */ |
710 | 0 | utfgridRenderPath(img, box); |
711 | |
|
712 | 0 | return MS_SUCCESS; |
713 | 0 | } |
714 | | |
715 | 0 | static int utfgridFreeSymbol(symbolObj *) { return MS_SUCCESS; } |
716 | | |
717 | | /* |
718 | | * Add the necessary functions for UTFGrid to the renderer VTable. |
719 | | */ |
720 | 0 | int msPopulateRendererVTableUTFGrid(rendererVTableObj *renderer) { |
721 | 0 | renderer->default_transform_mode = MS_TRANSFORM_SIMPLIFY; |
722 | |
|
723 | 0 | renderer->createImage = &utfgridCreateImage; |
724 | 0 | renderer->freeImage = &utfgridFreeImage; |
725 | 0 | renderer->saveImage = &utfgridSaveImage; |
726 | |
|
727 | 0 | renderer->startLayer = &utfgridStartLayer; |
728 | 0 | renderer->endLayer = &utfgridEndLayer; |
729 | |
|
730 | 0 | renderer->startShape = &utfgridStartShape; |
731 | 0 | renderer->endShape = &utfgridEndShape; |
732 | |
|
733 | 0 | renderer->renderPolygon = &utfgridRenderPolygon; |
734 | 0 | renderer->renderGlyphs = &utfgridRenderGlyphs; |
735 | 0 | renderer->renderLine = &utfgridRenderLine; |
736 | 0 | renderer->renderVectorSymbol = &utfgridRenderVectorSymbol; |
737 | 0 | renderer->renderPixmapSymbol = &utfgridRenderPixmapSymbol; |
738 | 0 | renderer->renderEllipseSymbol = &utfgridRenderEllipseSymbol; |
739 | |
|
740 | 0 | renderer->freeSymbol = &utfgridFreeSymbol; |
741 | |
|
742 | 0 | renderer->loadImageFromFile = msLoadMSRasterBufferFromFile; |
743 | |
|
744 | 0 | return MS_SUCCESS; |
745 | 0 | } |