Opened 14 years ago

Last modified 14 years ago

#3241 new enhancement

templates: getting a shape postion that can be used for labelling

Reported by: assefa Owned by: sdlime
Priority: normal Milestone: 6.0 release
Component: MapServer C Library Version: unspecified
Severity: normal Keywords: template postion
Cc: j.l.h.hartmann@…, warmerdam

Description

The initial idea is to be able to use a template with a combination of a WMS GetFeatureInfo request, retrieve attribute information as well as position info and display a point and possibly a label on the features on the client side. MapServer Templating supports at this point shpmid (and shpmidx, shpmidy) but the returning position might not be suitable to position a point/label on the client side. What we would need would be the possibility to return a position suitable for queried shapes that would be equivalent to the label positions computed in MapServer. Would it make send to add a new tag (shplabelpos) or extend shmpmid with an option? Looking in the docs and code source, I did not see a suitable tag to achieve this.

Attachments (4)

Test of GetFeatureInfo (National Parks).kmz (2.0 KB ) - added by assefa 14 years ago.
example of GetFeatureInfo using kml template
kit.map (1.9 KB ) - added by janh 14 years ago.
bug3241.patch (1.1 KB ) - added by assefa 14 years ago.
patch for invalid shapes
labelpos.gz (1016 bytes ) - added by janh 14 years ago.

Download all attachments as: .zip

Change History (31)

comment:1 by sdlime, 14 years ago

I might suggest the shpxy tag. It already supports a centroid option and adding a "labelpnt" option would be very easy. Note that this would output the computed label point, not the position (e.g. lower left-hand corner) of the label based on POSITION, FONT, SIZE, etc... parameters. I would think the client would have to figure that out on it's own based on the fonts it has access to, the resolution of it's display device and collisions with other labels.

Steve

comment:2 by assefa, 14 years ago

Sorry for commenting late on this. I realized looking in the code that there was the centroid support. I did add an option using the term labelpos. I will change it to labelpnt. I did not initially though that I needed to compute the position based on label parameters. I manly needed to get a decent label position for polygons using Google earth as a client. I will check if I can add support using the parameters.

by assefa, 14 years ago

example of GetFeatureInfo using kml template

comment:3 by assefa, 14 years ago

When using this feature in my case with GE wms + getfetureinfo, there is a need to clip the shapes to the map's extent before calculating the labelpnt so that It fits well onto of the image returned by GetMap. I think maybe adding an additional cliptoextent parameter would allow the user to chose this behavior. The example attached above shows and implementation forcing the calculation of the labelpnt based on features clipped by the map's extent.

comment:4 by janh, 14 years ago

Cc: j.l.h.hartmann@… added

Even better would be the extent of the label (x1,y1,x2,y2) next to or instead of the central point.

Jan

in reply to:  4 comment:5 by sdlime, 14 years ago

Replying to janh:

Even better would be the extent of the label (x1,y1,x2,y2) next to or instead of the central point.

Jan

Would that need to take collision avoidance into account? Kicking out the bbox of the label isn't that hard IF you can live with a non-cache processed label.

comment:6 by assefa, 14 years ago

I believe this can be extended to return the bbox.

To come back to my initial requirement, the first need for me is to be able to return a decent position (x,y) so that labeling (and possibly markers) can be done on the client side. In some cases it might make sense to return the computed label point (using label object setting such as POSITION, SIZE, ...). In other cases, only the label position is required (as an example, using this functionality with Google Earth through GetFeatureInfo, It would make more sense to return the label position, so that a marker and labels could be placed properly for all type of layers). Returning a computed point would not allow to do this.

I guess this can be controlled by a setting in the tag allowing both cases to work (something like labelpnt_use_label_settings).

comment:7 by janh, 14 years ago

Perhaps the following could be returned, based on options for the query template

  • bbox (x1,y1,x2,y2)
  • label point (x1,y1, proably the middle of the bbox)
  • displayed (boolean, whether the label is hidden by others or by the map boundary or is visible)
  • formatting options, like the shpxy and shpext templates, e.g. projection, and as many others as you like

comment:8 by assefa, 14 years ago

I think the idea is to integrate this functionality inside the shpxy as part of the options. There is already a support for centroid (undocumented) that returns x,y coordinates of the of the middle of the bbox for the shape:

[shpxy centroid=true ...]

This support would use the same concept where you can do (mutually exclusive with the centroid)

[shpxy labelpnt=true ...]

beside that, the two options that I would need would be:

  • do we clip the feature to the map extents before calculating the position (labelpnt_clip_to_map)
  • do we use the label attributes such as offset, position, ...(if available) to calculate the label point position (labelpnt_use_label_settings)

We could then have [shpxy labelpnt=true labelpnt_clip_to_map=false labelpnt_use_label_settings=false ....]

we could also have as a possible 3rd option [shpxy labelbbox=true ...] where the bbox could be returned

I believe the option clip_to_map could apply also to the labelbbox. I am less sure about the use_label_settings applying to the labelbbox but It could be. If that is the case the options name could be just clip_to_map and use_label_settings and applies to both options.

You also mention "hidden labels", I am assuming you mean label collusion detection? I am not sure that It would be possible to do it

comment:9 by assefa, 14 years ago

Steve,

would you see any issues with the proposed labelpnt support and the options to control the map clipping and the use of label settings?

comment:10 by sdlime, 14 years ago

Given the increased complexity since the start of the ticket I'd really prefer a separate "label" tag now (sorry). The default would give you the bbox of the label and a point="true" option get's you just the point. Make the most common use case the default and add a few other params for the more exotic cases. Otherwise I think the shpxy tag starts to become untenable... For example:

1. [label]
2. [label format="<rect>$minx,$miny</rect><rect>$maxx,$maxy</rect>"]
3. [label point="true" format="$x,$y"]

The format option would follow that used by the various extent tags (there's a processExtentTag() function to emulate).

Steve

comment:11 by janh, 14 years ago

what about (not sure about the format):

[label]

[label rect=$x1,$y1,$x2,$y2]

[label point=$x1,$y1]

format options:isvisible, proj=<image|world>, further [shpxy] options

And perhaps an options to link the label to the shape it comes from.

Jan

comment:12 by sdlime, 14 years ago

For tag name I'd do "shplabel" for consistency. It's possible, I suppose that you might want both the point AND bbox at the same time so you could do:

[shplabel format="bounds=[$minx,$miny,$maxy,$maxy], point=[$x,$y]"]

Which would output both in JSON for example.

Again, for consistency with other tags I'd stick with the format parameter (item and the extent tags use it).

Steve

comment:13 by assefa, 14 years ago

Ok I will re-change the code according to the comments and add a new shplabel tag. I believe the most common use would be to get a point so that would be the default use in the format ($x,$y). Other parameters such as precision, proj would also be available as well as clip to map and possibility or not to use label object settings.

comment:14 by assefa, 14 years ago

committed in r9729

shplabel tag with options:

  • format (default "$x,$y"). with the possibility to use $minx,$miny,$maxx,$maxy
  • proj
  • precision
  • clip_to_map (set true by default)
  • use_label_settings (set false by default)

comment:15 by sdlime, 14 years ago

I like it, nice and clean... Steve

comment:16 by janh, 14 years ago

It almost works. A query for an existing map on a point where four rectangles overlap, using a template with only [shplabel] gives:

nan,nan

The same point with nquery:

nan,nan nan,nan nan,nan nan,nan

comment:17 by assefa, 14 years ago

Jan,

would it be possible to get the test data/map/request. I will help me identify the issue. I have just committed a missing validation (r9732) but not sure if that will fix your problem.

by janh, 14 years ago

Attachment: kit.map added

comment:19 by assefa, 14 years ago

Jan,

Not sure what the problem is. If the label calculation fails for a feature, It should not replace the $x,$y by an invalid number. At least that's what the code tries to do.

You can send me directly if possible (assefa@…) a shape file (exact copy of your postgis data) for the layer kaartgrenzen and I can step in to code and see what happens for each shape that fits the selection criteria.

comment:20 by janh, 14 years ago

I finally got it. It's the MS_NINT_GENERIC macro in mapserver.h. On a 64 bit architecture it should return a *long* double

#define MS_NINT_GENERIC(x) ((x) >= 0.0 ? ((long double) ((x)+.5)) : ((long double) ((x)-.5)))

Can't say I really understand this, but it solves my problem in this case. I don't understand why this hasn't been noticed before, as MS_NINT is used in more files, but it won't hurt to change the definition in mapserver.h

Jan

comment:21 by janh, 14 years ago

Sorry, this is nonsense (although it works!). There is a problem with MS_NINT here, but I don't understand it (see http://trac.osgeo.org/mapserver/ticket/1716). In the meantime, the problem at this location can be solved by using the MS_MAP2IMAGE_IC_DBL macro instead of MS_MAP2IMAGE_IC in mapprimitive.c. The DBL macro doesn't use MS_NINT

comment:22 by janh, 14 years ago

Oh, and --disable-fast-nint as a configure option does *not* work in this case. I still think it has something to do with the 64 bit architecture (it worked with Assefa), but I don't see how to debug this further

comment:23 by janh, 14 years ago

It's simpler than I thought, nothing to do with 64/32 bit architectures. My test-map was a small rectangle within a map extent that covers the whole earth. This rectangle gets converted to pixel coordinates in line 1599 of maptemplate.c:

msTransformShapeToPixel(shape, layer->map->extent, cellsize);

The function "msTransformShapeToPixel" (in maptemplate.c) uses the macro MAP2IMAGE_X_IC to transform from world to image coordinates, which rounds the results to integers. As the rectangle is such a small part of the whole world, all four corners are transformed to almost the same value and rounded to exactly the same integer. As a result, the resulting shape is degenerate. When the MAP2IMAGE_X_IC_DBL macro is used instead, coordinates are not rounded and remain slightly different.

So, either use MAP2IMAGE_X_IC_DBL in msTransformShapeToPixel, or build in a test for degenerate (smaller than pixel-) shapes. I wonder why this error hasn't shown up before.

With this small adaptation maptemplate.c looks OK to me

Jan

comment:24 by assefa, 14 years ago

There was a bug reported with this when the tag was used more than once. It has been fixed in r10127

comment:25 by assefa, 14 years ago

Jan,

I think adding an extra test on the shape would be the less disruptive way of correcting this. I am attaching a patch where the shape bbox is tested after the shape is transformed to pixel to see if minx==maxx && miny==maxy. In that case we will consider the shape as invalid. I am attaching the patch since I can not reproduce the case here but If you still have handy your test environment, you can give it a try. I will then commit.

Thanks

by assefa, 14 years ago

Attachment: bug3241.patch added

patch for invalid shapes

comment:26 by janh, 14 years ago

Hi Assefa, this does not work. Before your patch, in line 1609, msTransformShapeToPixel transforms the shape from world to image coordinates. In my test case, a small rectangle is transformed from latlon to pixels. As this is done within an extent covering the whole world (-180/-90, 180/90), the image polygon is very small and its the corners are nearly the same. As floating point numbers they are still different, but msTransformShapeToPixel rounds them to integers. So the resulting image polygon has its four corners on exactly the same location.

In your patch, this lead to "labelposvalid" being set to false, and no label is computed.

However, even in this degenerate case "msComputeBounds()" returns valid coordinates, although they are the same for minx/maxx and miny/maxy. My solution would be to use those values to fill the labelPos structure:

if (shape->bounds.minx == shape->bounds.maxx && shape->bounds.miny == shape->bounds.maxy) {

labelposvalid = MS_TRUE; labelPos.x = shape->bounds.minx labelPos.y = shape->bounds.miny

}

I attach a mapfile and corresponding data. At the top of the mapfile is the query that doesn't work. It should fail with you too, as this bug has nothing to do with my different 64-bits architecture, as I thought at first.

by janh, 14 years ago

Attachment: labelpos.gz added

comment:27 by warmerdam, 14 years ago

Cc: warmerdam added

I'm seeing problem with raster query results (#3480) which seems to relate to r9716 applied for this ticket. I'll try to keep an eye on both tickets and I dig through this.

Note: See TracTickets for help on using tickets.