Details
Description
The provided patch adds support for rescaling symbolizers according to their Unit of Measure (UOM). This feature allows for symbolizers to have a "real-world" size (e.g., size given in meters or feet), so that they grow as you zoom in.
There are several issues in the implementation that may require a discussion before commiting:
- The approach used was to implement a UomRescaleStyleVisitor class for rescaling Symbolizers according to their Unit Of Measure (i.e., compute their sizes in pixels), and use that in SLDStyleFactory whenever a Style2D is created. In order to do that, the current map scale is needed (i.e., how screen units relate to real-world units, or how many meters are covered by each pixel). In this implementation, this scale was extracted from the SLDStyleFactory.createStyle's ScaleRange parameter, which was seen to be set to (scaleDenominator,scaleDenominator) in the renderers. However, maybe it would make more sense to pass on the scaleDenominator directly. Another issue is that the ScaleRange is measured in "meters per millimeters" and, in order to convert this to the needed "pixels per meter" value for UOM rescaling, we need a DPI parameter that is controllable via Hints. This is a little awkward, since the "pixels per meter" value could have been computed directly from the world2screen transform, without ever having to explicitly define DPI. Besides, the LabelCache does not have a Hints parameter. So, maybe it would be easier to pass around a scale parameter already measured in pixels per meter.
- Methods were added to RendererUtilities.java to help compute the pixels per meter ratio (in order to convert sizes in meters/feet/etc to pixels).
- In SLDStyleFactory, both createStyleInternal and createDynamicStyle were modified to rescale the given symbolizer. It was not clear whether createDynamicStyle is needed or where it is used.
- Style2D objects created by SLDStyleFactory should always be rescaled according to the UOM, but for one case: in Drawer.java (used for legend icons), the rendering must be done WITHOUT rescaling the symbolizer, since a legend has no scale. This was currently implemented adding a createStyleNoUOMRescaling method, because as such less GeoTools code would be changed. But maybe it would be better if SLDStyleFactory never rescaled anything and classes such as StreamingRenderer explicitly rescaled the symbolizer to pixels and then called SLDStyleFactory normally.
- The UomRescaleStyleVisitor could be discussed as well. For simplicity, overriding visit() methods to add rescaling was done only on the Symbolizer level, where the UOM parameter is available. The alternative would be to implement visit(Graphic), visit(Stroke), etc. and have the UOM parameter be included in the visitor's state.
Issue Links
- is related to
-
GEOT-1536
ImageLoader overhaul needed
-
Activity
Milton Jonathan
made changes -
| Field | Original Value | New Value |
|---|---|---|
| Description |
The provided patch adds support for rescaling symbolizers according to their Unit of Measure (UOM). This feature allows for symbolizers to have a "real-world" size (e.g., size given in meters or feet), so that they grow as you zoom in. There are several issues in the implementation that may require a discussion before commiting: - The approach used was to implement a UomRescaleStyleVisitor class for rescaling Symbolizers according to their Unit Of Measure (i.e., compute their sizes in pixels), and use that in SLDStyleFactory whenever a Style2D is created. In order to do that, the current map scale is needed (i.e., how screen units relate to real-world units, or how many meters are covered by each pixel). This was extracted from the SLDStyleFactory.createStyle's ScaleRange parameter, which was seen to be set to (scaleDenominator,scaleDenominator) in the renderers. However, maybe it would make more sense to pass on the scaleDenominator directly. - Methods were added to RendererUtilities.java to help compute the pixels per meter ratio (in order to convert sizes in meters/feet/etc to pixels). - In SLDStyleFactory, both createStyleInternal and createDynamicStyle were modified to rescale the given symbolizer. It was not clear whether createDynamicStyle is needed or where it is used. - Style2D objects created by SLDStyleFactory should always be rescaled according to the UOM, but for one case: in Drawer.java (used for legend icons), the rendering must be done WITHOUT rescaling the symbolizer, since a legend has no scale. This was currently implemented adding a createStyleNoUOMRescaling method, because as such less GeoTools code would be changed. But maybe it would be better if SLDStyleFactory never rescaled anything and classes such as StreamingRenderer explicitly rescaled the symbolizer to pixels and then called SLDStyleFactory normally. - The UomRescaleStyleVisitor could be discussed as well. For simplicity, overriding visit() methods to add rescaling was done only on the Symbolizer level, where the UOM parameter is available. The alternative would be to implement visit(Graphic), visit(Stroke), etc. and have the UOM parameter be included in the visitor's state. |
The provided patch adds support for rescaling symbolizers according to their Unit of Measure (UOM). This feature allows for symbolizers to have a "real-world" size (e.g., size given in meters or feet), so that they grow as you zoom in. There are several issues in the implementation that may require a discussion before commiting: - The approach used was to implement a UomRescaleStyleVisitor class for rescaling Symbolizers according to their Unit Of Measure (i.e., compute their sizes in pixels), and use that in SLDStyleFactory whenever a Style2D is created. In order to do that, the current map scale is needed (i.e., how screen units relate to real-world units, or how many meters are covered by each pixel). In this implementation, this scale was extracted from the SLDStyleFactory.createStyle's ScaleRange parameter, which was seen to be set to (scaleDenominator,scaleDenominator) in the renderers. However, maybe it would make more sense to pass on the scaleDenominator directly. Another issue is that the ScaleRange is measured in "meters per millimeters" and, in order to convert this to the needed "pixels per meter" value for UOM rescaling, we need a DPI parameter that is controllable via Hints. This is a little awkward, since the "pixels per meter" value could have been computed directly from the world2screen transform, without ever having to explicitly define DPI. Besides, the LabelCache does not have a Hints parameter. So, maybe it would be easier to pass around a scale parameter already measured in pixels per meter. - Methods were added to RendererUtilities.java to help compute the pixels per meter ratio (in order to convert sizes in meters/feet/etc to pixels). - In SLDStyleFactory, both createStyleInternal and createDynamicStyle were modified to rescale the given symbolizer. It was not clear whether createDynamicStyle is needed or where it is used. - Style2D objects created by SLDStyleFactory should always be rescaled according to the UOM, but for one case: in Drawer.java (used for legend icons), the rendering must be done WITHOUT rescaling the symbolizer, since a legend has no scale. This was currently implemented adding a createStyleNoUOMRescaling method, because as such less GeoTools code would be changed. But maybe it would be better if SLDStyleFactory never rescaled anything and classes such as StreamingRenderer explicitly rescaled the symbolizer to pixels and then called SLDStyleFactory normally. - The UomRescaleStyleVisitor could be discussed as well. For simplicity, overriding visit() methods to add rescaling was done only on the Symbolizer level, where the UOM parameter is available. The alternative would be to implement visit(Graphic), visit(Stroke), etc. and have the UOM parameter be included in the visitor's state. |
Milton Jonathan
made changes -
| Attachment | geotools-tecgraf.2.6.x.uom_rescaling.review1.patch [ 48023 ] |
Andrea Aime
made changes -
| Attachment | GEOT-2964.patch [ 48172 ] |
Jody Garnett
made changes -
| Fix Version/s | 2.6.3 [ 16260 ] | |
| Fix Version/s | 2.6.4 [ 16316 ] |
Andrea Aime
made changes -
Andrea Aime
made changes -
| Attachment | RescaleImage.java [ 48916 ] |
Andrea Aime
made changes -
| Attachment | GEOT-2964.patch [ 48925 ] |
Andrea Aime
made changes -
| Attachment | GEOT-2964.patch [ 49040 ] |
Andrea Aime
made changes -
| Status | Open [ 1 ] | In Progress [ 3 ] |
Jody Garnett
made changes -
| Fix Version/s | 2.6.5 [ 16493 ] | |
| Fix Version/s | 2.6.4 [ 16316 ] |
Andrea Aime
made changes -
| Status | In Progress [ 3 ] | Resolved [ 5 ] |
| Resolution | Fixed [ 1 ] |
Andrea Aime
made changes -
| Status | Resolved [ 5 ] | Closed [ 6 ] |
Jody Garnett
made changes -
| Component/s | main [ 10810 ] | |
| Component/s | core styling [ 11424 ] |
First a clarification about the dynamic styles: yes, as far as I know they are not used anymore, we should remove them. A long time ago symbolizers that depended on feature attributes were turned into these dynamic styles to avoid many object creations, then JVM became faster at dealing with garbage collection and we switched back, but the code that creates the dynamic styles was not removed. Since those methods are public we have to deprecate them first and remove them on trunk.
Now back to the patch. There are a few things that are not working or that could be done better.
The UomRescaleStyleVisitor does not seem to handle styles that do depend on feature own attributes. In general the rescaling code should check if the Expression is a Literal, in that case extracting the value using evaluate(null, Double.class) is the right approach. But if the Expression is not a Literal, it should be concatenated with a multiplication by the desired rescale factor instead (use FilterFactory.multiply to generate that).
All the modifications in SLDStyleFactory are not really necessary in my opinion. You should get the same result with less work, both coding wise and runtime wise, by applying the visitor in StreamingRenderer.processStylers (either against the original style, or, better, at least for big styles with many scale dependencies, against the active rules in the selected feature type styles). This should take care of the legend drawing as well, no need to modify it.
That also removes the need to work with scale ranges, since in that method you have the scale denominator already computed.
About the idea of computing the pixels per meter range directly from the world to scren transform... I thinking about it, but it does not add up for me. That transform goes from the units of the data to the screen, what if the data is expressed in feet, or worse, it's expressed in geographic coordinates? The scale factor of the affine transform is expressed in pixel/native unit, it's not a pure number (whilst the real map scale should be a pure number).
The geographic coordinates case is also important since the scale factor used in the WMS specification is "odd" as it basically assumes the Earth is a cylinder instead of a ball (yes, I know, it's wrong, but that's how the spec is made and it's there for good reasons), but to be consistent with the rest of the renderer, in my opinion you should start from the scale denominator as computed (if you don't specify the OGC compliance hint you'll get a proper scale, though it will also fail very quickly to compute the moment you get out of the area of definition of the current projection... which again, should not happen in a perfect world, but users are far from aware of these issues)
By the way, I don't see anywhere in the code some real units handling. What if the UOM specified in the symbolizer and the one in the data do not match? For example, what happens if the the user specifies the UOM in the symbolizer as feet?
Finally, there is an oddity in the UomRescaleStyleVisitorTest. Code like:
{code}
double rescaledSize = rescaledPolySymb.getStroke().getWidth().evaluate(filterFactory, Double.class);
{code}
does not make sense to me. Why pass the filterFactory as a parameter to evaluate? That method expects a Feature, or null if there is none available.
When fixing the visitor to be aware of generic expressions (functions, use of feature attributes) you should also make at least one test checking that the visitor builds the right multiplication, and you can also, eventually, build a feature that you can pass in those evaluates to check the results are ok (assuming a style that uses feature attributes, that is).