OpenWaterFoundation / owf-app-infomapper-ng

Open Water Foundation InfoMapper web application for menu-driven maps and visualizations, using Angular
GNU General Public License v3.0
1 stars 2 forks source link

Improve mouse-over highlighting for polygons #403

Open smalers opened 3 years ago

smalers commented 3 years ago

The InfoMapper extensively uses GeoJSON with Leaflet. However, one problem is that polygon shapes don't always render well when highlighting polygons. For example, see the image below from:

http://poudre.openwaterfoundation.org/latest/#/map/entities-basins

My understanding is that highlighting is typically done my detecting the feature to highlight, change its Leaflet styling, let it render as highlighted, and then set the style back to original once not highlighted.

The problem is that the polygons have overlapping borders (coordinates are repeated in each polygon) and the order of polygon in the data controls the rendering order. One solution would be to ensure that highlighted features are always drawn on top of the specific layer, maybe by drawing the entire layer and then only the highlighted feature using highlighted style. Is this possible without overly-complicating the code? The SNODAS web application uses bringToFront to ensure that highlighted and selected features are drawn on top but this may not work with multiple layers or more complex maps?

InfoMapper uses an extended GeoLayer for selected features and map marker. Should there be a dynamic list of highlighted features? This list would perhaps only contain the single feature currently under the mouse pointer. It would be nice to not change the styling on features over and over again just due to moving the mouse, but creating shapes dynamically could be slow and result of a lot of memory being used. Maybe there is a way to internally use a pointer to the shape data for drawing but specify different styling. That way memory would not need to be manipulated for shape data.

A different solution is to use a data format such as TopoJSON (for example, see: Topojson on a Leaflet map), which has only a single line when shared between polygons. The benefits of TopoJSON are that files are smaller (since polygons share lines) and performance is faster. Downsides are that additional processing needs to occur to create the file, and software would need to be updated to handle. Would also solve the issue of highlights? I'm thinking NO because the draw order of the polygons would still be an issue.

Maybe it does make sense to add a highlighted features list to the extended layer class to handle this also. Then the original layer would always render using its style and highlights, selects, and additional markers (such as from address search) would be rendered on top of the layer. This seems reasonable for display-only software. Interactive editing might be harder to deal with but currently we don't do editing.

image

smalers commented 3 years ago

To follow up on a conversation, it seems like the most robust way to do highlighting is to handle similar to how selected features are handled, in which case the extended layer class object instance will maintain a list of highlighted features. Below are some technical considerations.

  1. Defaulting to yellow, thick line, with no fill color for polygons seems to work well. Avoiding fill helps ensure that the original color is used for the feature, which is good to avoid confusion.
  2. If a dynamic list is used for highlighted features, it will need to be refreshed quickly so that user experience is good with mouse movements. The following are options for handling the shape for highlighting.
    1. Have a reference to the original layer shape and change its style. This is bad because it will only redraw within the layer and has the overlapping line problem - not good.
    2. Copy the shape from the original layer into a "highlighted features layer", with separate memory and styling. This is OK but may have performance issues because it takes more memory and time to copy.
      1. If copy is done, it could be a shallow or deep copy. See Cloning an array in Javascript/Typescript
        1. Create a highlight list that has reference to the original feature data for coordinates but has new styling information. This would allow reusing some data and save on memory and performance hit. This could be fast and minimize memory use but may be more complicated to code and need to be careful that Leaflet does not modify what the reference is pointing to.
smalers commented 3 years ago

After some discussion, I think we should take a step back and really evaluate the general design for selection and highlighting. Below are some thoughts. The design should to a degree be independent of Leaflet features, but of course is impacted by Leaflet features and limitations.

First, rendering a layer may involve several visual components:

  1. The layer itself, with appropriate style and legend. Layers are drawn in the map in the order from background, polygons, lines, points, with the map creator responsible for ordering layers intelligently.
  2. Selected features, which should be drawn on top of the layer. Selected features should be obvious but not obscure underlying data.
  3. Highlighted features, which should be drawn on top of the layer and selected features. Highlighted features should be obvious and not obscure underlying layer or selection features.
  4. Annotations, which are additional graphics such address marker and search radius. I'm not sure if Leaflet has a true annotation ability or relies on things like Circle layer for drawing radius, etc. An example of annotations is user-supplied mark-up of a map such as text labels, lines, and other basic shapes.

All of the above are specified to Leaflet, which then renders/draws the data. Is it possible to let custom code do some rendering without depending on Leaflet to do so? Maybe, but assume no and that the above need to be created.

The Leaflet documentation has vector Layer classes including Path base class and other classes derived from it such as Polygon, Polyline, and Circle. The GeoJSON layer helps ingest GeoJSON data files and must handle the different geometries.

There are several requirements for layer interactions, including:

  1. Mouse-over, which results in highlighting and information display. Typically only one feature at a time is highlighted in a layer if features do not overlap, although it is possible that features may overlap and therefore multiple features will highlight. Highlighting is transient.
  2. Selection, either through data table query or mouse click. It is possible and often likely that more than one feature will be selected at a time. Selection is "sticky" until selections are cleared.
  3. Tool that interacts with features, such as editing - this is currently not a focus of the InfoMapper but may be added in the future.

In addition to the visible map, there are several design considerations, including:

  1. Regardless of the implementation, the conceptual design should involve a map, with layer groups, each of which contains layers. Straying from this concept will complicate things. Most if not all GIS tools use these basic concepts.
  2. The "layer" can be treated as the data layer, selection, and highlight data. This can be accomplished by using objects that relate to each other, for example:
    • Extending a main Leaflet layer class to contain selection and highlight data. Maybe this does not work because Leaflet does not know how to render each? Management of data is as data members in the extended class.
    • Extending a new Leaflet layer class to contain selection data with reference to the main layer (similar for highlight data). Management of data is by treating the several layer objects as related, perhaps by looking up using a common identifier associated with the main data layer?
      1. If extra layers are used to manage selections and highlights (rather than properties in the single data layer):
    • Need to insert in the proper order so they draw correctly.
    • Legend needs to ignore (or maybe show in the future).
    • Need to create the selection and highlight layers. It would seem that using the simpler Polygon layer rather than full GeoJSON layer would make sense.
    • Need to always treat as a group so user never seems selection or highlight layers without the underlying data layer.
    • Need to implement code in a way that performs well and is not fragile. For example, use a utility class to help.
    • Needs to work consistently regardless of geometry type.
Nightsphere commented 3 years ago

The new code has been merged with some changes. The code is quite similar to what I had before, but has some changes of how things are being made.

The selected layer is made with a separate geoJSON layer, added to a MapLayerItem (layerItem) object, and added before the layerItem on the map. This way, a layer toggle will effect both the Leaflet and selected features on the layer. For now it is not being added on top of a feature, so the mouseover and mouseout events won't be affected. This is the same for both polygons and points.

As of now the highlighting has been reverted back to an updating of each feature's style.

The issue where certain Dialogs were only allowed to open once should be taken care now. Let me know if something is still off.