Permalink
Browse files

implementation of offsets on follow labels (#4399)

  • Loading branch information...
tbonfort committed Jul 2, 2012
1 parent e178db7 commit 7633072f664bfc6e88bb555f4dbc3d8373a249f4
Showing with 187 additions and 91 deletions.
  1. +10 −0 HISTORY.TXT
  2. +5 −0 configure
  3. +6 −0 configure.in
  4. +11 −6 mapdraw.c
  5. +21 −0 mapgeos.c
  6. +28 −2 mapprimitive.c
  7. +5 −0 mapserver.h
  8. +101 −83 maputil.c
View
@@ -15,6 +15,11 @@ For a complete change history, please see the Git log comments.
Current Version (git master, 6.3-dev, future 6.4):
--------------------------------------------------
- implementation of offsets on follow labels (#4399)
6.2.0 release (git branch-6-2) 2012/11/14:
--------------------------------------------------
- Fix WFS GetFeature result bounds are not written in requested projection (#4494)
- Fixed wrong size in LegendURL of root layer (#4441)
@@ -40,6 +45,11 @@ Current Version (git master, 6.3-dev, future 6.4):
- Fixed the OGR driver to use point or line spatial filter geometries in degenerated cases (#4420)
- implement OFFSET x -99 on ANGLE FOLLOW labels (#4399)
Version 6.2 (beta1: 20120629):
-------------------------------------------------
- Fix WFS filter is produced as non-standard XML (#4171)
- Fix pixmap symbol loading issue (#4329)
View
@@ -18321,6 +18321,11 @@ $as_echo "no" >&6; }
GEOS_INCDIR=`$GEOS_CONFIG --includes`
GEOS_INC="-I$GEOS_INCDIR"
if test $ac_geos_version -ge 300300; then
GEOS_ENABLED="-DUSE_GEOS -DHAVE_GEOS_OFFSET_CURVE"
fi
if test $ac_geos_version -ge 300300; then
GEOS_LIB="`$GEOS_CONFIG --clibs`"
else
View
@@ -1000,6 +1000,12 @@ if test -n "$with_geos" -a "$with_geos" != "no" ; then
GEOS_INCDIR=`$GEOS_CONFIG --includes`
GEOS_INC="-I$GEOS_INCDIR"
dnl Geos >=3.3 has SingleSidedBuffer for offset lines
if test $ac_geos_version -ge 300300; then
GEOS_ENABLED="-DUSE_GEOS -DHAVE_GEOS_OFFSET_CURVE"
fi
dnl Geos >=3.3 has a new config option: --clibs.
if test $ac_geos_version -ge 300300; then
GEOS_LIB="`$GEOS_CONFIG --clibs`"
View
@@ -2947,12 +2947,17 @@ int msDrawLabelCache(imageObj *image, mapObj *map)
/* TODO: treat the case with multiple labels and/or leader lines */
}
/* apply offset and buffer settings */
label_offset_x = labelPtr->offsetx*scalefactor;
label_offset_y = labelPtr->offsety*scalefactor;
label_buffer = (labelPtr->buffer*image->resolutionfactor);
label_mindistance = (labelPtr->mindistance*image->resolutionfactor);
/* apply offset and buffer settings */
if(labelPtr->anglemode != MS_FOLLOW) {
label_offset_x = labelPtr->offsetx*scalefactor;
label_offset_y = labelPtr->offsety*scalefactor;
} else {
label_offset_x = 0;
label_offset_y = 0;
}
label_buffer = (labelPtr->buffer*image->resolutionfactor);
label_mindistance = (labelPtr->mindistance*image->resolutionfactor);
#ifdef oldcode
/* adjust the baseline (see #1449) */
if(labelPtr->type == MS_TRUETYPE) {
View
@@ -656,6 +656,27 @@ void msGEOSFreeWKT(char* pszGEOSWKT)
#endif
}
shapeObj *msGEOSOffsetCurve(shapeObj *p, double offset) {
#if defined USE_GEOS && defined HAVE_GEOS_OFFSET_CURVE
GEOSGeom g1, g2;
if(!p)
return NULL;
if(!p->geometry) /* if no geometry for the shape then build one */
p->geometry = (GEOSGeom) msGEOSShape2Geometry(p);
g1 = (GEOSGeom) p->geometry;
if(!g1) return NULL;
g2 = GEOSOffsetCurve(g1, offset, 4, GEOSBUF_JOIN_MITRE, fabs(offset*1.5));
return msGEOSGeometry2Shape(g2);
#else
msSetError(MS_GEOSERR, "GEOS support is not available.", "msGEOSingleSidedBuffer()");
return NULL;
#endif
}
/*
** Analytical functions exposed to MapServer/MapScript.
*/
View
@@ -1748,12 +1748,34 @@ labelPathObj** msPolylineLabelPath(mapObj *map, imageObj *img,shapeObj *p, int m
segment_index = max_line_index = 0;
total_length = max_line_length = 0.0;
if(!string) return NULL;
if(!string) return NULL;
labelpaths = (labelPathObj **) msSmallMalloc(sizeof(labelPathObj *) * labelpaths_size);
(*regular_lines) = (int *) msSmallMalloc(sizeof(int) * regular_lines_size);
if(label->offsetx != 0 && (label->offsety == -99 || label->offsety == 99)) {
double offset;
if(label->offsetx > 0) {
offset = label->offsetx + label->size/2;
} else {
offset = label->offsetx - label->size/2;
}
if(label->offsety == 99 && p->numlines>0 && p->line[0].numpoints > 0) {
/* is the line mostly left-to-right or right-to-left ?
* FIXME this should be done line by line, by stepping through shape->lines, however
* the OffsetPolyline function works on shapeObjs, not lineObjs
* we only check the first line
*/
if(p->line[0].point[0].x < p->line[0].point[p->line[0].numpoints-1].x) {
/* line is left to right */
offset = -offset;
}
}
p = msOffsetPolyline(p,offset, -99);
if(!p) return NULL;
}
msPolylineComputeLineSegments(p, &segment_lengths, &line_lengths, &max_line_index, &max_line_length, &segment_index, &total_length);
if(label->repeatdistance > 0)
@@ -1777,6 +1799,10 @@ labelPathObj** msPolylineLabelPath(mapObj *map, imageObj *img,shapeObj *p, int m
/* set the number of paths in the array */
*numpaths = labelpaths_index;
*num_regular_lines = regular_lines_index;
if(label->offsety == -99 && label->offsetx != 0) {
msFreeShape(p);
msFree(p);
}
return labelpaths;
}
@@ -1810,7 +1836,7 @@ void msPolylineLabelPathLineString(mapObj *map, imageObj *img, shapeObj *p, int
labelPathObj *labelpath = NULL;
/* Line smoothing kernel */
double kernel[] = {0.1, 0.2, 2, 0.2, 0.1}; /* {1.5, 2, 15, 2, 1.5}; */
double kernel[] = {0.1,0.2,2,0.2,0.1}; /* {1.5, 2, 15, 2, 1.5}; */
double kernel_normal = 2.6; /* Must be sum of kernel elements */
int kernel_size = 5;
View
@@ -2871,6 +2871,11 @@ extern "C" {
#define MS_IMAGE_RENDERER_CACHE(im) MS_RENDERER_CACHE(MS_IMAGE_RENDERER((im)))
#define MS_MAP_RENDERER(map) ((map)->outputformat->vtable)
shapeObj *msOffsetCurve(shapeObj *p, double offset);
#if defined HAVE_GEOS_OFFSET_CURVE
shapeObj *msGEOSOffsetCurve(shapeObj *p, double offset);
#endif
#ifdef __cplusplus
}
#endif
View
184 maputil.c
@@ -199,7 +199,7 @@ static void bindLabel(layerObj *layer, shapeObj *shape, labelObj *label, int dra
/* check the label styleObj's (TODO: do we need to use querymapMode here? */
for(i=0; i<label->numstyles; i++) {
/* force MS_DRAWMODE_FEATURES for label styles */
bindStyle(layer, shape, label->styles[i], drawmode|MS_DRAWMODE_FEATURES);
bindStyle(layer, shape, label->styles[i], drawmode|MS_DRAWMODE_FEATURES);
}
if(label->numbindings > 0) {
@@ -1617,8 +1617,6 @@ void msTransformPoint(pointObj *point, rectObj *extent, double cellsize,
}
/*
** Helper functions supplied as part of bug #2868 solution. Consider moving these to
** mapprimitive.c for more general use.
@@ -1714,17 +1712,22 @@ static double point_cross(const pointObj a, const pointObj b)
return a.x*b.y-a.y*b.x;
}
/*
** For offset corner point calculation 1/sin() is used
** to avoid 1/0 division (and long spikes) we define a
** limit for sin().
*/
#define CURVE_SIN_LIMIT 0.3
shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety)
shapeObj *msOffsetCurve(shapeObj *p, double offset)
{
int i, j, first,idx;
#if defined HAVE_GEOS_OFFSET_CURVE
ret = msGEOSOffsetCurve(p,offset);
/* GEOS curve offsetting can fail sometimes, we continue with our own implementation
if that is the case.*/
if(ret)
return ret;
#endif
/*
** For offset corner point calculation 1/sin() is used
** to avoid 1/0 division (and long spikes) we define a
** limit for sin().
*/
#define CURVE_SIN_LIMIT 0.3
int i, j, first,idx,ok=0;
shapeObj *ret = (shapeObj*)msSmallMalloc(sizeof(shapeObj));
msInitShape(ret);
ret->numlines = p->numlines;
@@ -1733,81 +1736,96 @@ shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety)
ret->line[i].numpoints=p->line[i].numpoints;
ret->line[i].point=(pointObj*)msSmallMalloc(sizeof(pointObj)*ret->line[i].numpoints);
}
if(offsety == -99) { /* complex calculations */
int ok = 0;
for (i = 0; i < p->numlines; i++) {
pointObj old_pt, old_diffdir, old_offdir;
if(p->line[i].numpoints<2) {
ret->line[i].numpoints = 0;
continue; /* skip degenerate lines */
}
ok =1;
/* initialize old_offdir and old_diffdir, as gcc isn't smart enough to see that it
* is not an error to do so, and prints a warning */
old_offdir.x=old_offdir.y=old_diffdir.x=old_diffdir.y = 0;
idx=0;
first = 1;
/* saved metrics of the last processed point */
if (p->line[i].numpoints>0)
old_pt=p->line[i].point[0];
for(j=1; j<p->line[i].numpoints; j++) {
const pointObj pt = p->line[i].point[j]; /* place of the point */
const pointObj diffdir = point_norm(point_diff(pt,old_pt)); /* direction of the line */
const pointObj offdir = point_rotz90(diffdir); /* direction where the distance between the line and the offset is measured */
pointObj offpt; /* this will be the corner point of the offset line */
/* offset line points computation */
if(first == 1) { /* first point */
first = 0;
offpt = point_sum(old_pt,point_mul(offdir,offsetx));
} else { /* middle points */
/* curve is the angle of the last and the current line's direction (supplementary angle of the shape's inner angle) */
double sin_curve = point_cross(diffdir,old_diffdir);
double cos_curve = point_cross(old_offdir,diffdir);
if ((-1.0)*CURVE_SIN_LIMIT < sin_curve && sin_curve < CURVE_SIN_LIMIT) {
/* do not calculate 1/sin, instead use a corner point approximation: average of the last and current offset direction and length */
/*
** TODO: fair for obtuse inner angles, however, positive and negative
** acute inner angles would need special handling - similar to LINECAP
** to avoid drawing of long spikes
*/
offpt = point_sum(old_pt, point_mul(point_sum(offdir, old_offdir),0.5*offsetx));
} else {
double base_shift = -1.0*(1.0+cos_curve)/sin_curve;
offpt = point_sum(old_pt, point_mul(point_sum(point_mul(diffdir,base_shift),offdir), offsetx));
}
for (i = 0; i < p->numlines; i++) {
pointObj old_pt, old_diffdir, old_offdir;
if(p->line[i].numpoints<2) {
ret->line[i].numpoints = 0;
continue; /* skip degenerate points */
}
ok = 1;
/* initialize old_offdir and old_diffdir, as gcc isn't smart enough to see that it
* is not an error to do so, and prints a warning */
old_offdir.x=old_offdir.y=old_diffdir.x=old_diffdir.y = 0;
idx=0;
first = 1;
/* saved metrics of the last processed point */
if (p->line[i].numpoints>0)
old_pt=p->line[i].point[0];
for(j=1; j<p->line[i].numpoints; j++) {
const pointObj pt = p->line[i].point[j]; /* place of the point */
const pointObj diffdir = point_norm(point_diff(pt,old_pt)); /* direction of the line */
const pointObj offdir = point_rotz90(diffdir); /* direction where the distance between the line and the offset is measured */
pointObj offpt; /* this will be the corner point of the offset line */
/* offset line points computation */
if(first == 1) { /* first point */
first = 0;
offpt = point_sum(old_pt,point_mul(offdir,offset));
} else { /* middle points */
/* curve is the angle of the last and the current line's direction (supplementary angle of the shape's inner angle) */
double sin_curve = point_cross(diffdir,old_diffdir);
double cos_curve = point_cross(old_offdir,diffdir);
if ((-1.0)*CURVE_SIN_LIMIT < sin_curve && sin_curve < CURVE_SIN_LIMIT) {
/* do not calculate 1/sin, instead use a corner point approximation: average of the last and current offset direction and length */
/*
** TODO: fair for obtuse inner angles, however, positive and negative
** acute inner angles would need special handling - similar to LINECAP
** to avoid drawing of long spikes
*/
offpt = point_sum(old_pt, point_mul(point_sum(offdir, old_offdir),0.5*offset));
} else {
double base_shift = -1.0*(1.0+cos_curve)/sin_curve;
offpt = point_sum(old_pt, point_mul(point_sum(point_mul(diffdir,base_shift),offdir), offset));
}
ret->line[i].point[idx]=offpt;
idx++;
old_pt=pt;
old_diffdir=diffdir;
old_offdir=offdir;
}
ret->line[i].point[idx]=offpt;
idx++;
old_pt=pt;
old_diffdir=diffdir;
old_offdir=offdir;
}
/* last point */
if(first == 0) {
pointObj offpt=point_sum(old_pt,point_mul(old_offdir,offsetx));
ret->line[i].point[idx]=offpt;
idx++;
}
/* last point */
if(first == 0) {
pointObj offpt=point_sum(old_pt,point_mul(old_offdir,offset));
ret->line[i].point[idx]=offpt;
idx++;
}
if(idx != p->line[i].numpoints) {
/* printf("shouldn't happen :(\n"); */
ret->line[i].numpoints=idx;
ret->line=msSmallRealloc(ret->line,ret->line[i].numpoints*sizeof(pointObj));
}
if(idx != p->line[i].numpoints) {
/* printf("shouldn't happen :(\n"); */
ret->line[i].numpoints=idx;
ret->line=msSmallRealloc(ret->line,ret->line[i].numpoints*sizeof(pointObj));
}
if(!ok) ret->numlines = 0; /* all lines where degenerate */
} else { /* normal offset (eg. drop shadow) */
for (i = 0; i < p->numlines; i++) {
for(j=0; j<p->line[i].numpoints; j++) {
ret->line[i].point[j].x=p->line[i].point[j].x+offsetx;
ret->line[i].point[j].y=p->line[i].point[j].y+offsety;
}
}
if(!ok) ret->numlines = 0; /* all lines where degenerate */
return ret;
}
shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety)
{
int i, j;
shapeObj *ret;
if(offsety == -99) { /* complex calculations */
return msOffsetCurve(p,offsetx);
}
ret = (shapeObj*)msSmallMalloc(sizeof(shapeObj));
msInitShape(ret);
ret->numlines = p->numlines;
ret->line=(lineObj*)msSmallMalloc(sizeof(lineObj)*ret->numlines);
for(i=0; i<ret->numlines; i++) {
ret->line[i].numpoints=p->line[i].numpoints;
ret->line[i].point=(pointObj*)msSmallMalloc(sizeof(pointObj)*ret->line[i].numpoints);
}
for (i = 0; i < p->numlines; i++) {
for(j=0; j<p->line[i].numpoints; j++) {
ret->line[i].point[j].x=p->line[i].point[j].x+offsetx;
ret->line[i].point[j].y=p->line[i].point[j].y+offsety;
}
}

0 comments on commit 7633072

Please sign in to comment.