Ticket #3241 (new enhancement)

Opened 2 years ago

Last modified 23 months ago

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

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

Change History

  Changed 2 years ago by sdlime

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

  Changed 2 years ago by assefa

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.

Changed 2 years ago by assefa

example of GetFeatureInfo? using kml template

  Changed 2 years ago by assefa

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.

follow-up: ↓ 5   Changed 2 years ago by janh

  • 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   Changed 2 years ago by sdlime

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.

  Changed 2 years ago by assefa

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).

  Changed 2 years ago by janh

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

  Changed 2 years ago by assefa

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

  Changed 2 years ago by assefa

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?

  Changed 2 years ago by sdlime

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

  Changed 2 years ago by janh

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

  Changed 2 years ago by sdlime

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

  Changed 2 years ago by assefa

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.

  Changed 2 years ago by assefa

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)

  Changed 2 years ago by sdlime

I like it, nice and clean... Steve

  Changed 2 years ago by janh

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

  Changed 2 years ago by assefa

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.

Changed 2 years ago by janh

  Changed 2 years ago by assefa

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.

  Changed 2 years ago by janh

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

  Changed 2 years ago by janh

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

  Changed 2 years ago by janh

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

  Changed 2 years ago by janh

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

  Changed 2 years ago by assefa

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

  Changed 2 years ago by assefa

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

Changed 2 years ago by assefa

patch for invalid shapes

  Changed 2 years ago by janh

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.

Changed 2 years ago by janh

  Changed 23 months ago by warmerdam

  • 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.