From 326e3c84647230e918665ad06a8a2ba7659eb50a Mon Sep 17 00:00:00 2001 From: Matthieu Viry Date: Fri, 17 Feb 2023 14:44:34 +0100 Subject: [PATCH] Rewind rings of polygons before displaying layer --- client/js/helpers.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- client/js/layers.js | 5 +++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/client/js/helpers.js b/client/js/helpers.js index 51371f3e9..2bbbae3d9 100755 --- a/client/js/helpers.js +++ b/client/js/helpers.js @@ -1269,8 +1269,6 @@ function squareForceCollide() { return force; } - - export function parseTransformAttribute(t) { const d = {}; for (const i in t = t.match(/(\w+\((-?\d+\.?\d*e?-?\d*,?)+\))+/g)) { @@ -1279,3 +1277,45 @@ export function parseTransformAttribute(t) { } return d; } + +function rewindRing(ring, dir) { + let tArea = 0; + let err = 0; + // eslint-disable-next-line no-plusplus + for (let i = 0, len = ring.length, j = len - 1; i < len; j = i++) { + const k = (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]); + const m = tArea + k; + err += Math.abs(tArea) >= Math.abs(k) ? tArea - m + k : k - m + tArea; + tArea = m; + } + if (tArea + err >= 0 !== !!dir) ring.reverse(); +} + +function rewindRings(rings, outer) { + if (rings.length === 0) return; + rewindRing(rings[0], outer); + for (let i = 1; i < rings.length; i++) { + rewindRing(rings[i], !outer); + } +} + +// Rewind rings of geojson (Multi)Polygons +// adapted from https://github.com/mapbox/geojson-rewind +// (originally authored by Mapbox, under ISC Licence) +export function rewind(geojson, outer) { + if (!geojson.type || geojson.type !== 'FeatureCollection') { + throw new Error('Input must be a GeoJSON FeatureCollection'); + } + + for (let i = 0; i < geojson.features.length; i++) { + if (geojson.features[i].geometry.type === 'Polygon') { + rewindRings(geojson.features[i].geometry.coordinates, outer); + } else if (geojson.features[i].geometry.type === 'MultiPolygon') { + for (let j = 0; j < geojson.features[i].geometry.coordinates.length; j++) { + rewindRings(geojson.features[i].geometry.coordinates[j], outer); + } + } + } + + return geojson; +} diff --git a/client/js/layers.js b/client/js/layers.js index abed62678..c6f069633 100644 --- a/client/js/layers.js +++ b/client/js/layers.js @@ -5,7 +5,7 @@ import { check_remove_existing_box, make_confirm_dialog2 } from './dialogs'; import { check_layer_name } from './function'; import { create_li_layer_elem, display_error_during_computation, - isValidJSON, make_box_type_fields, request_data, setSelected, xhrequest, + isValidJSON, make_box_type_fields, request_data, rewind, setSelected, xhrequest, } from './helpers'; import { valid_join_check_display } from './join_popup'; import { zoom_without_redraw } from './map_ctrl'; @@ -346,12 +346,13 @@ export function add_layer_topojson(text, options = {}) { }); const func_data_idx = (_, ix) => `feature_${ix}`; + const features = rewind(topojson.feature(topoObj, topoObj_objects), true).features; map.insert('g', '.legend') .attrs({ id: lyr_id, class: data_to_load ? 'targeted_layer layer' : 'layer' }) .styles({ 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }) .selectAll('.subunit') - .data(topojson.feature(topoObj, topoObj_objects).features, d => d.id) + .data(features, d => d.id) .enter() .append('path') .attrs({ d: path_to_use, id: func_data_idx })