1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: RFC48 implementation of geometry transformations for styling
6 * Author: Thomas Bonfort , Camptocamp (thomas.bonfort at camptocamp.com)
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 #include "mapthread.h"
32
33 extern int yyparse(parseObj *p);
34
msStyleSetGeomTransform(styleObj * s,char * transform)35 void msStyleSetGeomTransform(styleObj *s, char *transform)
36 {
37 msFree(s->_geomtransform.string);
38 if (!transform) {
39 s->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
40 s->_geomtransform.string = NULL;
41 return;
42 }
43 s->_geomtransform.string = msStrdup(transform);
44 if(!strncasecmp("start",transform,5)) {
45 s->_geomtransform.type = MS_GEOMTRANSFORM_START;
46 } else if(!strncasecmp("end",transform,3)) {
47 s->_geomtransform.type = MS_GEOMTRANSFORM_END;
48 } else if(!strncasecmp("vertices",transform,8)) {
49 s->_geomtransform.type = MS_GEOMTRANSFORM_VERTICES;
50 } else if(!strncasecmp("bbox",transform,4)) {
51 s->_geomtransform.type = MS_GEOMTRANSFORM_BBOX;
52 } else if(!strncasecmp("labelpnt",transform,8)) {
53 s->_geomtransform.type = MS_GEOMTRANSFORM_LABELPOINT;
54 } else if(!strncasecmp("labelpoly",transform,9)) {
55 s->_geomtransform.type = MS_GEOMTRANSFORM_LABELPOLY;
56 } else if(!strncasecmp("labelcenter",transform,11)) {
57 s->_geomtransform.type = MS_GEOMTRANSFORM_LABELCENTER;
58 } else if(!strncasecmp("centroid",transform,8)) {
59 s->_geomtransform.type = MS_GEOMTRANSFORM_CENTROID;
60 } else {
61 s->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
62 msSetError(MS_MISCERR,"unknown transform expression","msStyleSetGeomTransform()");
63 msFree(s->_geomtransform.string);
64 s->_geomtransform.string = NULL;
65 }
66 }
67
68
69 /*
70 * return a copy of the geometry transform expression
71 * returned char* must be freed by the caller
72 */
msStyleGetGeomTransform(styleObj * s)73 char *msStyleGetGeomTransform(styleObj *s)
74 {
75 return msStrdup(s->_geomtransform.string);
76 }
77
78
calcOrientation(pointObj * p1,pointObj * p2)79 double calcOrientation(pointObj *p1, pointObj *p2)
80 {
81 double theta;
82 theta = atan2(p2->x - p1->x,p2->y - p1->y);
83 return MS_RAD_TO_DEG*(theta-MS_PI2);
84 }
85
calcMidAngle(pointObj * p1,pointObj * p2,pointObj * p3)86 double calcMidAngle(pointObj *p1, pointObj *p2, pointObj *p3)
87 {
88 pointObj p1n;
89 double dx12, dy12, dx23, dy23, l12, l23;
90
91 /* We treat both segments as vector 1-2 and vector 2-3 and
92 * compute their dx,dy and length
93 */
94 dx12 = p2->x - p1->x;
95 dy12 = p2->y - p1->y;
96 l12 = sqrt(dx12*dx12 + dy12*dy12);
97 dx23 = p3->x - p2->x;
98 dy23 = p3->y - p2->y;
99 l23 = sqrt(dx23*dx23 + dy23*dy23);
100
101 /* Normalize length of vector 1-2 to same as length of vector 2-3 */
102 if (l12 > 0.0)
103 {
104 p1n.x = p2->x - dx12*(l23/l12);
105 p1n.y = p2->y - dy12*(l23/l12);
106 }
107 else
108 p1n = *p2; /* segment 1-2 is 0-length, use segment 2-3 for orientation */
109
110 /* Return the orientation defined by the sum of the normalized vectors */
111 return calcOrientation(&p1n, p3);
112 }
113
114 /*
115 * RFC48 implementation:
116 * - transform the original shapeobj
117 * - use the styleObj to render the transformed shapeobj
118 */
msDrawTransformedShape(mapObj * map,imageObj * image,shapeObj * shape,styleObj * style,double scalefactor)119 int msDrawTransformedShape(mapObj *map, imageObj *image, shapeObj *shape, styleObj *style, double scalefactor)
120 {
121 int type = style->_geomtransform.type;
122 int i,j,status = MS_SUCCESS;
123 switch(type) {
124 case MS_GEOMTRANSFORM_END: /*render point on last vertex only*/
125 for(j=0; j<shape->numlines; j++) {
126 lineObj *line = &(shape->line[j]);
127 pointObj *p = &(line->point[line->numpoints-1]);
128 if(p->x<0||p->x>image->width||p->y<0||p->y>image->height)
129 continue;
130 if(style->autoangle==MS_TRUE && line->numpoints>1) {
131 style->angle = calcOrientation(&(line->point[line->numpoints-2]),p);
132 }
133 status = msDrawMarkerSymbol(map,image,p,style,scalefactor);
134 }
135 break;
136 case MS_GEOMTRANSFORM_START: /*render point on first vertex only*/
137 for(j=0; j<shape->numlines; j++) {
138 lineObj *line = &(shape->line[j]);
139 pointObj *p = &(line->point[0]);
140 /*skip if outside image*/
141 if(p->x<0||p->x>image->width||p->y<0||p->y>image->height)
142 continue;
143 if(style->autoangle==MS_TRUE && line->numpoints>1) {
144 style->angle = calcOrientation(p,&(line->point[1]));
145 }
146 status = msDrawMarkerSymbol(map,image,p,style,scalefactor);
147 }
148 break;
149 case MS_GEOMTRANSFORM_VERTICES:
150 for(j=0; j<shape->numlines; j++) {
151 lineObj *line = &(shape->line[j]);
152 for(i=1; i<line->numpoints-1; i++) {
153 pointObj *p = &(line->point[i]);
154 /*skip points outside image*/
155 if(p->x<0||p->x>image->width||p->y<0||p->y>image->height)
156 continue;
157 if(style->autoangle==MS_TRUE) {
158 style->angle = calcMidAngle(&(line->point[i-1]),&(line->point[i]),&(line->point[i+1]));
159 }
160 status = msDrawMarkerSymbol(map,image,p,style,scalefactor);
161 }
162 }
163 break;
164 case MS_GEOMTRANSFORM_BBOX: {
165 shapeObj bbox;
166 lineObj bbox_line;
167 pointObj bbox_points[5];
168 int padding = MS_MAX(style->width,style->size)+3; /* so clipped shape does not extent into image */
169
170 /*create a shapeObj representing the bounding box (clipped by the image size)*/
171 bbox.numlines = 1;
172 bbox.line = &bbox_line;
173 bbox.line->numpoints = 5;
174 bbox.line->point = bbox_points;
175
176 msComputeBounds(shape);
177 bbox_points[0].x=bbox_points[4].x=bbox_points[1].x =
178 (shape->bounds.minx < -padding) ? -padding : shape->bounds.minx;
179 bbox_points[2].x=bbox_points[3].x =
180 (shape->bounds.maxx > image->width+padding) ? image->width+padding : shape->bounds.maxx;
181 bbox_points[0].y=bbox_points[4].y=bbox_points[3].y =
182 (shape->bounds.miny < -padding) ? -padding : shape->bounds.miny;
183 bbox_points[1].y=bbox_points[2].y =
184 (shape->bounds.maxy > image->height+padding) ? image->height+padding : shape->bounds.maxy;
185 status = msDrawShadeSymbol(map, image, &bbox, style, scalefactor);
186 }
187 break;
188 case MS_GEOMTRANSFORM_CENTROID: {
189 double unused; /*used by centroid function*/
190 pointObj centroid;
191 if(MS_SUCCESS == msGetPolygonCentroid(shape,¢roid,&unused,&unused)) {
192 status = msDrawMarkerSymbol(map,image,¢roid,style,scalefactor);
193 }
194 }
195 break;
196 case MS_GEOMTRANSFORM_EXPRESSION: {
197 int status;
198 shapeObj *tmpshp;
199 parseObj p;
200
201 p.shape = shape; /* set a few parser globals (hence the lock) */
202 p.expr = &(style->_geomtransform);
203
204 if(p.expr->tokens == NULL) { /* this could happen if drawing originates from legend code (#5193) */
205 status = msTokenizeExpression(p.expr, NULL, NULL);
206 if(status != MS_SUCCESS) {
207 msSetError(MS_MISCERR, "Unable to tokenize expression.", "msDrawTransformedShape()");
208 return MS_FAILURE;
209 }
210 }
211
212 p.expr->curtoken = p.expr->tokens; /* reset */
213 p.type = MS_PARSE_TYPE_SHAPE;
214
215 status = yyparse(&p);
216 if (status != 0) {
217 msSetError(MS_PARSEERR, "Failed to process shape expression: %s", "msDrawTransformedShape", style->_geomtransform.string);
218 return MS_FAILURE;
219 }
220 tmpshp = p.result.shpval;
221
222 switch (tmpshp->type) {
223 case MS_SHAPE_POINT:
224 case MS_SHAPE_POLYGON:
225 status = msDrawShadeSymbol(map, image, tmpshp, style, scalefactor);
226 break;
227 case MS_SHAPE_LINE:
228 status = msDrawLineSymbol(map, image, tmpshp, style, scalefactor);
229 break;
230 }
231
232 msFreeShape(tmpshp);
233 msFree(tmpshp);
234 }
235 break;
236 case MS_GEOMTRANSFORM_LABELPOINT:
237 case MS_GEOMTRANSFORM_LABELPOLY:
238 break;
239 default:
240 msSetError(MS_MISCERR, "unknown geomtransform", "msDrawTransformedShape()");
241 return MS_FAILURE;
242 }
243 return status;
244 }
245
246 /*
247 * RFC89 implementation:
248 * - transform directly the shapeobj
249 */
msGeomTransformShape(mapObj * map,layerObj * layer,shapeObj * shape)250 int msGeomTransformShape(mapObj *map, layerObj *layer, shapeObj *shape)
251 {
252 int i;
253 expressionObj *e = &layer->_geomtransform;
254
255 #ifdef USE_V8_MAPSCRIPT
256 if (!map->v8context) {
257 msV8CreateContext(map);
258 if (!map->v8context)
259 {
260 msSetError(MS_V8ERR, "Unable to create v8 context.", "msGeomTransformShape()");
261 return MS_FAILURE;
262 }
263 }
264
265 msV8ContextSetLayer(map, layer);
266 #endif
267
268 switch(e->type) {
269 case MS_GEOMTRANSFORM_EXPRESSION: {
270 int status;
271 shapeObj *tmpshp;
272 parseObj p;
273
274 p.shape = shape; /* set a few parser globals (hence the lock) */
275 p.expr = e;
276 p.expr->curtoken = p.expr->tokens; /* reset */
277 p.type = MS_PARSE_TYPE_SHAPE;
278 p.dblval = map->cellsize * (msInchesPerUnit(map->units,0)/msInchesPerUnit(layer->units,0));
279 p.dblval2 = 0;
280 /* data_cellsize is only set with contour layer */
281 if (layer->connectiontype == MS_CONTOUR)
282 {
283 const char *value = msLookupHashTable(&layer->metadata, "__data_cellsize__");
284 if (value)
285 p.dblval2 = atof(value);
286 }
287
288 status = yyparse(&p);
289 if (status != 0) {
290 msSetError(MS_PARSEERR, "Failed to process shape expression: %s", "msGeomTransformShape()", e->string);
291 return MS_FAILURE;
292 }
293
294 tmpshp = p.result.shpval;
295
296 for (i= 0; i < shape->numlines; i++)
297 free(shape->line[i].point);
298 shape->numlines = 0;
299 if (shape->line) free(shape->line);
300
301 for(i=0; i<tmpshp->numlines; i++)
302 msAddLine(shape, &(tmpshp->line[i])); /* copy each line */
303
304 msFreeShape(tmpshp);
305 msFree(tmpshp);
306 }
307 break;
308 default:
309 msSetError(MS_MISCERR, "unknown geomtransform", "msGeomTransformShape()");
310 return MS_FAILURE;
311 }
312
313 return MS_SUCCESS;
314 }
315