Esri / arcgis-maps-sdk-dotnet-toolkit

Toolkit for ArcGIS Maps SDK for .NET
https://esri.github.io/arcgis-maps-sdk-dotnet-toolkit/
Apache License 2.0
217 stars 121 forks source link

popup: Simplify HTML tree to fix spacing #527

Closed mstefarov closed 1 year ago

mstefarov commented 1 year ago

This is a result of a little background project I've been working on since July. This PR teaches our HTML parser to simplify the document structure and remove most spacing-related quirks that plague HTML. It also implements support for display:none attribute and unbreakable spaces ( ), seen in a number of Living Atlas maps.

New code walks the node tree, skips empty/useless nodes, and merges as many single-child nodes as possible. It then finds groups of inline nodes that flow together, and adjusts trailing/leading whitespace among and around these nodes.

Although this adds some complexity to our HTML parser, it benefits all supported platforms in terms of fidelity (more accurate rendering) and performance (much fewer UI element needed to render a popup). Here are some concrete examples:

Example 1: Social Vulnerability Index

https://www.arcgis.com/apps/mapviewer/index.html?webmap=2c8fdc6267e4439e968837020e7618f3 This popup has a number of empty paragraphs, which were getting collapsed by browsers, but wasted a lot of space in PopupViewer.

Before:After:
Popup HTML ```html

2018 Overall SVI Score:

0.82

Possible scores range from 0 (lowest vulnerability) to 1 (highest vulnerability).

A score of 0.82 indicates high vulnerability.

Data Dictionary

```
Original document tree ``` Document { children=1 } Block { children=9 } Block { children=1 } Span { children=1 } Span { size=1 children=1 } Text { text="2018 Overall SVI Score:" } Block { children=1 } Span { bold=True children=1 } Span { size=1.5 children=1 } Text { text="0.82" } Block { children=1 } Span { size=1 children=1 } Span { } Block { children=1 } Span { size=1 children=5 } Span { children=1 } Text { text="Possible scores range from " } Span { bold=True children=1 } Text { text="0 " } Span { children=1 } Text { text="(lowest vulnerability) to " } Span { bold=True children=1 } Text { text="1" } Span { children=1 } Text { text=" (highest vulnerability)." } Block { children=1 } Span { size=1 children=1 } Span { } Block { children=1 } Span { size=1 children=5 } Span { children=1 } Text { text="A score of " } Span { bold=True children=1 } Text { text="0.82 " } Span { children=1 } Text { text="indicates " } Span { bold=True children=1 } Text { text="high " } Span { children=1 } Text { text="vulnerability." } Block { children=1 } Span { size=1 children=1 } Span { } Block { children=1 } Link { color=Color [A=255, R=109, G=109, B=109] text="https://svi.cdc.gov/Documents/Data/2018_SVI_Data/SVI2018Documentation.pdf" children=1 } Span { children=1 } Span { size=1 children=1 } Text { text="Data Dictionary " } Block { children=1 } Span { } ```
Simplified document tree ``` Document { children=1 } Block { children=5 } Block { children=1 } Text { size=1 text="2018 Overall SVI Score:" } Block { children=1 } Text { bold=True size=1.5 text="0.82" } Block { size=1 children=5 } Text { text="Possible scores range from " } Text { bold=True text="0 " } Text { text="(lowest vulnerability) to " } Text { bold=True text="1" } Text { text=" (highest vulnerability)." } Block { size=1 children=5 } Text { text="A score of " } Text { bold=True text="0.82 " } Text { text="indicates " } Text { bold=True text="high " } Text { text="vulnerability." } Block { children=1 } Link { color=Color [A=255, R=109, G=109, B=109] text="https://svi.cdc.gov/Documents/Data/2018_SVI_Data/SVI2018Documentation.pdf" children=1 } Text { size=1 text="Data Dictionary" } ```

Example 2: NWS Wind Speed forecast

https://www.arcgis.com/apps/mapviewer/index.html?webmap=a7b007939f02406ca2b8559a821c08ab These popups have many ignorable line break and extra spaces/newlines between paragraphs, which caused bad spacing and bloated the visual tree.

Before:After:
Popup HTML ```html

Wind Speed Forecast

Fresh Breeze (18-24mph, 29-38km/h)

From

10/12/2023 02:00 PM

Until

10/12/2023 04:58 PM

Source: The National Digital Forecast Database produced by the National Weather Service

Data updated every 3 hours

```
Original document tree ``` Document { children=17 } Block { align=Center children=4 } Span { bold=True children=1 } Span { size=1.5 children=1 } Text { text="Wind Speed Forecast" } Span { size=0.75 children=2 } Break { } Text { text=" " } Span { size=0.8333333333333334 children=1 } Text { text=" " } Span { size=0.75 } Text { text=" " } Block { align=Center children=2 } Span { bold=True children=1 } Span { size=1.125 children=1 } Span { color=Color [A=255, R=0, G=0, B=255] children=1 } Text { text="Fresh Breeze (18-24mph, 29-38km/h)" } Span { size=0.75 } Text { text=" " } Block { children=1 } Span { size=1 children=1 } Text { text=" " } Text { text=" " } Block { align=Center children=2 } Span { size=1.125 children=1 } Text { text="From" } Span { size=0.75 } Text { text=" " } Block { align=Center children=2 } Span { bold=True children=1 } Span { size=0.75 children=3 } Text { text="10/12/2023 02:00 PM" } Break { } Text { text=" " } Span { size=0.75 } Text { text=" " } Block { align=Center children=2 } Span { size=1.125 children=1 } Text { text="Until" } Span { size=0.75 } Text { text=" " } Block { align=Center children=2 } Span { bold=True children=1 } Span { size=0.75 children=1 } Text { text="10/12/2023 04:58 PM" } Span { italic=True children=1 } Span { size=0.75 color=Color [A=255, R=105, G=105, B=105] } Text { text=" " } Block { children=1 } Span { italic=True children=1 } Span { size=0.75 color=Color [A=255, R=105, G=105, B=105] children=3 } Text { text="Source: The National Digital Forecast Database produced by the National Weather Service" } Break { } Text { text=" " } Text { text=" " } Block { children=1 } Span { italic=True children=1 } Span { size=0.75 color=Color [A=255, R=105, G=105, B=105] children=1 } Text { text="Data updated every 3 hours" } ```
Simplified document tree ``` Document { children=8 } Block { align=Center children=1 } Text { bold=True size=1.5 text="Wind Speed Forecast" } Block { align=Center children=1 } Text { bold=True size=1.125 color=Color [A=255, R=0, G=0, B=255] text="Fresh Breeze (18-24mph, 29-38km/h)" } Block { align=Center children=1 } Text { size=1.125 text="From" } Block { bold=True size=0.75 align=Center children=1 } Text { text="10/12/2023 02:00 PM" } Block { align=Center children=1 } Text { size=1.125 text="Until" } Block { align=Center children=1 } Text { bold=True size=0.75 text="10/12/2023 04:58 PM" } Block { italic=True size=0.75 color=Color [A=255, R=105, G=105, B=105] children=1 } Text { text="Source: The National Digital Forecast Database produced by the National Weather Service" } Block { children=1 } Text { italic=True size=0.75 color=Color [A=255, R=105, G=105, B=105] text="Data updated every 3 hours" } ```

Example 3: Energielabels (Netherlands Enterprise Agency)

https://www.arcgis.com/apps/mapviewer/index.html?webmap=22f951f0951f4922b800021b0ba98539 These popups have many collapsible table rows/cells and hidden elements, which caused Runtime to render completely differently from the browser.

Before:After:
Popup HTML ```html Er is 1 energielabel geregistreerd in dit pand met bouwjaar 1888

Meest zuinig
Minst zuinig

Dit pand bevat in totaal 9 verblijfsobject(en), met de volgende gebruiksfunctie(s):
 
• Winkelfunctie: 0
• Woonfunctie: 8
• Kantoorfunctie: 0
• Bijeenkomstfunctie: 1
• Gezondheidszorgfunctie: 0
• Industriefunctie: 0
• Logiesfunctie: 0
• Celfunctie: 0
• Onderwijsfunctie: 0
• Sportfunctie: 0
• Overige gebruiksfuncties: 0
```
Original document tree ``` Document { children=14 } Text { text="Er is 1 energielabel geregistreerd in dit pand met bouwjaar 1888" } Break { } Break { } Text { text=" " } Table { children=5 } TableRow { children=2 } TableCell { children=3 } Text { text=" " } Image { text="https://arcgis.com/sharing/rest/content/items/f179492056fe42318fa0948a9c38d138/data" } Text { text=" " } TableCell { children=2 } Text { text=" " } Block { } TableRow { children=2 } TableCell { children=2 } Text { text=" " } Image { text="https://arcgis.com/sharing/rest/content/items/f179492056fe42318fa0948a9c38d138/data" } TableCell { children=2 } Text { text=" Meest zuinig " } Block { } TableRow { } TableRow { children=2 } TableCell { children=2 } Text { text=" " } Image { text="https://arcgis.com/sharing/rest/content/items/f179492056fe42318fa0948a9c38d138/data" } TableCell { children=1 } Text { text=" Minst zuinig" } TableRow { children=2 } TableCell { children=2 } Text { text=" " } Image { text="https://arcgis.com/sharing/rest/content/items/f179492056fe42318fa0948a9c38d138/data" } TableCell { children=2 } Text { text=" " } Block { } Break { } Text { text="Dit pand bevat in totaal " } Span { bold=True children=1 } Text { text="9" } Text { text=" verblijfsobject(en), met de volgende gebruiksfunctie(s):" } Break { } Text { text=" " } Break { } Text { text=" " } Table { children=11 } TableRow { children=1 } TableCell { children=2 } Text { text="• Winkelfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Woonfunctie: 8" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Kantoorfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Bijeenkomstfunctie: 1" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Gezondheidszorgfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Industriefunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Logiesfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Celfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Onderwijsfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=2 } Text { text="• Sportfunctie: 0" } Break { } TableRow { children=1 } TableCell { children=1 } Text { text="• Overige gebruiksfuncties: 0" } ```
Simplified document tree ``` Document { children=10 } Text { text="Er is 1 energielabel geregistreerd in dit pand met bouwjaar 1888" } Break { } Table { children=2 } TableRow { } TableRow { children=2 } TableCell { children=1 } Image { text="https://arcgis.com/sharing/rest/content/items/f179492056fe42318fa0948a9c38d138/data" } TableCell { } Break { } Text { text="Dit pand bevat in totaal " } Text { bold=True text="9" } Text { text=" verblijfsobject(en), met de volgende gebruiksfunctie(s):" } Break { } Text { text=" " } Table { children=2 } TableRow { children=1 } TableCell { children=1 } Text { text="• Woonfunctie: 8" } TableRow { children=1 } TableCell { children=1 } Text { text="• Bijeenkomstfunctie: 1" } ```