Skip to content

Commit

Permalink
Polygon hatch fills, legend improvements
Browse files Browse the repository at this point in the history
- new feature: Polygon hatch fill patterns as the way of distinction between class symbols (thanks to https://github.com/samanbey/leaflet-hatchclass). Has three modes: 'width', 'angle' and 'both'. Width mode alters only the alternating stroke widths per class symbol, angle mode only the alternating stroke angle per class symbol, and both alters both properties to make class symbols distinct
-- new option `strokeColors`: two colors of the alternating lines, as an array (example: ['darkred', 'none'])
-- new option `strokeWidth`: object, with min and max properties, both with a number value, when distinctionMode: 'width' or both'. Defines hatch min/max widths to alternate between, for individual classes (example: {min: 2, max: 10}). Tip: set to -1 to have solid fills on two ends of the symbols' spectrum, only in distinctionMode: 'width' and 'both'
-- new option `distinctionMode`: symbol distinction type between classes: 'angle'/'width'/'both'
-- new option `angle`: initial angle of lines in pattern
-- new option `alternateAngle`: value to increment angle with between hatch fill symbols. Warns if chosen alternating angle may result in very similar of duplicate symbols.
-- note: a workaround was needed to apply hatch fill pattern to polygons due to Leaflet ignoring `className` in layer.setStyle(). See Leaflet/Leaflet#2662. Currently, the workaround is fine and is working as expected
- new option `polygonMode`: polygon mode now supports two modes: 'color' and 'hatch' (default: 'color')
- new option `legendRowGap`: legend symbology row gap in pixels. You can also alter this in the attached CSS file. (default: 3)
- `style`/`radius`: defines marker shape radius (size), use only in point/color mode (default: 8, max: 10-12)
- due to a smaller redesign of the legend symbology, rows are now contained in a flexbox and now have a row-gap of 3px by default. This results in symbols in polygon modes getting separated a bit. Can be overridden with option `legendRowGap`, or by editing the main CSS file directly (class ".hatchPatch")
- function _stylePolygon() renamed to _stylePolygon_color(), to accomodate the existence of _stylePolygon_hatch()
- function _svgCreator(): removed hardcoded styling "margin-right: 10px", included in CSS now
- legend reflects custom point symbol size (`style`/`radius`) now
- reworked legend to support `polygonMode`
- legend now constructs its contents individually and appends content with div.appendChild() to the main legend div
- added dependency: https://github.com/samanbey/leaflet-hatchclass, included in ./lib/
- updated documentation and inline JSDoc
- updated main screenshots, added a new section and screenshot in documentation, showcasing hatch symbol distinction modes
- examples:
-- renamed example `polygons.html` to `polygons_c.html`
-- new example `polygons_h.html`: showcases the use of hatch fill patterns
-- new dataset `polygons_hatch_eu_lifeexp_2018.geojson`: for use in `polygons_h.html`
-- all: fixed inline comments
-- combined: swapped default layer OSM with CARTO Voyager
-- lines_c: swapped default layer OSM with CARTO Voyager, modified legend title
-- lines_w: swapped default layer OSM with CARTO Positron, modified some options
-- points_c: swapped default layer OSM with CARTO Voyager, modified legend title, added style/radius to showcase symbol size altering in point/color mode
  • Loading branch information
balladaniel committed Nov 17, 2023
1 parent 9367907 commit 5df9f94
Show file tree
Hide file tree
Showing 14 changed files with 926 additions and 146 deletions.
49 changes: 40 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Aims to simplify data visualization and creation of elegant thematic web maps wi
- Classification and styling of:
- Point features based on color and size (graduated symbol sizes)
- Line features based on line color and width (graduated line widths)
- Polygon features based on fill color (choropleth map)
- Polygon features based on fill color (choropleth map) and hatch fill pattern (thanks to [leaflet-hatchclass](https://github.com/samanbey/leaflet-hatchclass))
- Supported classification methods (mostly thanks to [simple-statistics.js](https://github.com/simple-statistics/simple-statistics)):
- natural breaks (Jenks)
- quantile (equal count)
Expand All @@ -27,6 +27,7 @@ Aims to simplify data visualization and creation of elegant thematic web maps wi
- rounding of class boundary values to n decimals or up/down to the nearest 10, 100, 1000 etc. numbers
- modifying class boundary values in legend by dividing/multiplying by a number (to easily change unit of measurement from m to km for example)
- positioning (L.control options)
- row gap adjustments

## Demo
All features in the examples listed here have binded tooltips (a default Leaflet feature) for an easier check of attribute values.
Expand All @@ -35,13 +36,15 @@ All features in the examples listed here have binded tooltips (a default Leaflet
- points (size, with diamond-shaped symbols): [./examples/points_s.html](https://balladaniel.github.io/leaflet-dataclassification/examples/points_s.html)
- lines (color): [./examples/lines_c.html](https://balladaniel.github.io/leaflet-dataclassification/examples/lines_c.html)
- lines (width): [./examples/lines_w.html](https://balladaniel.github.io/leaflet-dataclassification/examples/lines_w.html)
- polygons: [./examples/polygons.html](https://balladaniel.github.io/leaflet-dataclassification/examples/polygons.html)
- polygons (color): [./examples/polygons_c.html](https://balladaniel.github.io/leaflet-dataclassification/examples/polygons_c.html)
- polygons (hatch fill, with both width/angle as distinction): [./examples/polygons_h.html](https://balladaniel.github.io/leaflet-dataclassification/examples/polygons_h.html)

## Requirements
- [Leaflet](https://github.com/Leaflet/Leaflet) (tested with v1.9.4)
### External dependencies
- [simple-statistics.js](https://github.com/simple-statistics/simple-statistics) (tested with v7.8.0)
- [chroma.js](https://github.com/gka/chroma.js) (tested with v2.4.0)
- [leaflet-hatchclass](https://github.com/samanbey/leaflet-hatchclass)

Include dependencies plus `leaflet-dataclassification.css` and `leaflet-dataclassification.js` in your code. You can also link them through GitHub Pages:
``` html
Expand All @@ -62,6 +65,14 @@ const layer = L.dataClassification(data, {
pointShape: 'square',
lineMode: 'width',
lineWidth: {min: 1, max: 15},
polygonMode: 'color',
polygonHatch: {
strokeColors: ['lightgreen', '#fff8b5'],
strokeWidth: {min: -1, max: 13},
distinctionMode: 'both',
angle: 45,
alternateAngle: 45
},
colorRamp: 'OrRd',
colorCustom: ['rgba(210,255,178,1)', '#fec44f', 'f95f0eff'], // if specified, overrides colorRamp!
noDataColor: '#101010',
Expand All @@ -71,6 +82,7 @@ const layer = L.dataClassification(data, {
classRounding: 2,
legendTitle: 'Density (pop/km²)',
legendPosition: 'bottomleft',
legendRowGap: 5,
legendAscending: false,
legendTemplate: {
highest: '{low} and above [{count}]',
Expand All @@ -81,16 +93,17 @@ const layer = L.dataClassification(data, {
unitModifier: {action: 'divide', by: 1000},
style: {
fillColor: 'purple', // marker fill color in point/size mode
color: '#aabbcc', // line stroke color in line/width mode
weight: 5, // line stroke weight in line/color mode
fillOpacity: 0.7, // polygon fill opacity in polygon mode
radius: 8, // marker shape radius (size) in point/color mode,
fillOpacity: 0.7, // polygon fill opacity in polygon modes
color: '#aabbcc', // line stroke color in line/width mode, polygon outline stroke color in polygon modes
weight: 5, // line stroke weight in line/color mode, polygon outline stroke weight in polygon modes
}
}.addTo(map);
```
### Required options
- `mode <string>`: ['jenks'|'quantile'|'equalinterval'|'manual'] classification method: jenks, quantile, equalinterval, manual. When using manual (which partially defeats the purpose of this plugin), option `classes` must be an array of class boundary values!
- `classes <integer|array>`: desired number of classes (min: 3; max: 10 or featurecount, whichever is lower. If higher, reverts back to the max of 10.). If `mode` is manual, this must be an array of numbers (for example [50, 150, 200] would yield the following three classes: below 150, 150-200, above 200).
- `classes <integer|array>`: desired number of classes (min: 3; max: 10 or featurecount, whichever is lower. If higher, reverts back to the max of 10.). If `mode` is manual, this must be an array of numbers (for example [0, 150, 200] would yield the following three classes: below 150, 150-200, above 200).
- `field <string>`: target attribute field name to base classification on. Case-sensitive!
### Additional options (in addition to the standard L.geoJSON options)
Expand All @@ -102,6 +115,7 @@ const layer = L.dataClassification(data, {
- `pointShape <string>`: ['circle'|'square'|'diamond'] shape of points: 'circle', 'square', 'diamond' (default: 'circle')
- `style <object>`: custom styling
- `fillColor <string>`: marker fill color, use only in size mode (default: orange)
- `radius <float>`: marker shape radius (size), use only in color mode (default: 8, max: 10-12)
#### Specific for Line features
- `lineMode <string>`: ['color'|'width'] stroke "color" or "width" (default: 'color')
- `lineWidth <object>`: when lineMode: "width", define min/max stroke width as object
Expand All @@ -111,11 +125,22 @@ const layer = L.dataClassification(data, {
- `color <string>`: line stroke color, use only in width mode (default: blue, the L.path default)
- `weight <float>`: line stroke weight, use only in color mode (default: 3, the L.path default)
#### Specific for Polygon features
- `polygonMode <string>`: ['color`|`hatch`] fill "color" or "hatch" (default: 'color')
- `polygonHatch <object>`: when polygonMode: "hatch", customize hatch fill pattern
- `distinctionMode <string>`: ['width'|'angle'|'both'] symbol distinction type between classes (default: 'both')
- `strokeColors <array<string>>`: stroke colors (default: ['darkred', 'none'])
- `strokeWidth <object>`: stroke widths to gradually alternate between for symbols, when distinctionMode: 'width' or both'.
- `min <float>`: stroke width of the first color. Tip: set to -1 to have solid fills on two ends of the symbols' spectrum, only in distinctionMode: 'width' and 'both'. (default: 2)
- `max <float>`: stroke width of the other color (default: 10)
- `angle <number>`: initial angle for hatch pattern (leaflet-hatchclass default: 45)
- `alternateAngle <number>`: value to increment angle with between all hatch fill symbols, when distinctionMode: 'angle' or both'
- `style <object>`: custom styling
- `fillOpacity <float>`: polygon fill opacity, use only polygon mode (default: 0.7)
- `fillOpacity <float>`: polygon fill opacity (default: 0.7)
- `color <string>`: polygon outline color (default: '#3388ff' blue, the L.path default)
- `weight <float>`: polygon outline stroke width (default: 3, the L.path default)
#### General options
- `colorRamp <string>`: color ramp to use for symbology. Based on ColorBrewer2 color ramps (https://colorbrewer2.org/), included in Chroma.js. Custom colors (`colorCustom`) override this. (default: 'PuRd')
- `colorRamp <string>`: color ramp to use for symbology (only used with modes in which color is the way of distinction between symbols). Based on ColorBrewer2 color ramps (https://colorbrewer2.org/), included in Chroma.js. Custom colors (`colorCustom`) override this. (default: 'PuRd')
- `colorCustom <array<string>>`: custom color ramp defined as an array, colors in formats supported by Chroma.js, with opacity support. A minimum of two colors are required. If defined, custom colors override `colorRamp`. Example: ['rgba(210,255,178,1)', '#fec44f', 'f95f0eff']. Examples for yellow in different color formats: '#ffff00', 'ffff00', '#ff0', 'yellow', '#ffff0055', 'rgba(255,255,0,0.35)', 'hsla(58,100%,50%,0.6)', chroma('yellow').alpha(0.5). For more formats, see: https://gka.github.io/chroma.js/. For an interactive color palette helper, see: https://gka.github.io/palettes/.
- `noDataColor <string>`: fill/line color to use for features with null/nodata attribute values. (default: '#606060')
- `noDataIgnore <boolean>`: if true, features with null attribute values are not shown on the map. This also means the legend will not have a nodata class (default: false)
Expand All @@ -124,6 +149,7 @@ const layer = L.dataClassification(data, {
- `classRounding <integer>`: class boundary value rounding. When positive numbers are used for this option, class boundary values are rounded to x decimals, zero will round to whole numbers, while negative numbers will round values to the nearest 10, 100, 1000, etc. Example: with a setting of "1", a value of 254777.253 will get rounded up to 254777.3, with "0" it will be 254777, with "-2" it will become 254800. (default: null - no rounding happens, values are used as-is)
- `legendTitle <string>`: legend header (usually a description of visualized data, with a unit of measurement). HTML-markdown and styling allowed. To hide header, set this as ''. (by default it inherits target attribute field name, on which the classification is based on)
- `legendPosition <string>`: ['topleft'|'topright'|'bottomleft'|'bottomright'] legend position, L.control option. (default: 'bottomleft')
- `legendRowGap <number>`: legend symbology row gap in pixels. You can also alter this in the attached CSS file. (default: 3)
- `legendAscending <boolean>`: if true, value classes in legend will be ascending (low first, high last) (default: false)
- `legendTemplate <object>`: custom HTML formatting of legend rows using {high}, {low} and {count} placeholders (interpreted as high/low value and feature count in the context of a given class interval). Distinct formatting for the highest, lowest and middle class intervals. Middle class format requires both {high} and {low}, highest only {low} and lowest only {high}. You can also format the row for nodata, if there are features with null attributes and you wish to show a class for them in the legend (defined by `noDataIgnore`).
- `highest <string>`: template for the upper end of classes, "highest value and above" (default: '{low} <')
Expand All @@ -132,4 +158,9 @@ const layer = L.dataClassification(data, {
- `nodata <string>`: text to show for null/nodata class (default: 'No data')
- `unitModifier <object>`: modifies the final class boundary values in order to multiply/divide them by a number. Useful for example when a dataset attribute is in metres, but kilometres would fit the legend better (786000 metres shown as 786 km). Purely visual, only affects legend. Happens after classRounding.
- `action <string>`: ['divide'|'multiply'] action to take on the number specified by `by`. Required for `unitModifier`.
- `by <number>`: a number to divide/multiply class boundary values with. Required for `unitModifier`.
- `by <number>`: a number to divide/multiply class boundary values with. Required for `unitModifier`.
## Hatch fill pattern types
Hatch fill patterns provided by [leaflet-hatchclass](https://github.com/samanbey/leaflet-hatchclass). (Gede, M.: Hatch Fill on Webmaps – to Do or Not to Do, and How to Do, Abstr. Int. Cartogr. Assoc., 5, 48, https://doi.org/10.5194/ica-abs-5-48-2022, 2022.)
![Screenshot of different hatch fill pattern modes (distinctionMode option). Samples.](screenshots_hatchfill.png)
2 changes: 1 addition & 1 deletion examples/combined.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"CARTO Positron": cartodb_positron
};
// map div object, main Leaflet object:
var map = L.map('map', {layers: [OSM]}).setView([0,0], 3);
var map = L.map('map', {layers: [cartodb_voyager]}).setView([0,0], 3);
map.attributionControl.setPrefix('<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">Leaflet ' + L.version + '</a>');
map.createPane('front');
map.getPane('front').style.zIndex = 450;
Expand Down
Loading

0 comments on commit 5df9f94

Please sign in to comment.