Skip to content

Commit

Permalink
Fix broken polygons (#44)
Browse files Browse the repository at this point in the history
* updated deps

* fixed paths in index.html

* replaced buffer with clone as buffer breaks some polygons

* updated deps

* fixed some polygons handling

* updated deps

* added tolerance computation

* improved displaying polygons
  • Loading branch information
zdila authored Sep 17, 2024
1 parent 3f6d4fc commit 401ae0f
Show file tree
Hide file tree
Showing 8 changed files with 631 additions and 865 deletions.
16 changes: 8 additions & 8 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ <h1>MapTiler Geocoding Control Examples</h1>

<h2>Svelte</h2>
<ul>
<li><a href="examples/maptiler-sdk/">MapTiler SDK</a></li>
<li><a href="examples/maplibregl/">MapLibre GL JS</a></li>
<li><a href="examples/leaflet/">Leaflet</a></li>
<li><a href="examples/openlayers/">OpenLayers</a></li>
<li><a href="/examples/maptiler-sdk/">MapTiler SDK</a></li>
<li><a href="/examples/maplibregl/">MapLibre GL JS</a></li>
<li><a href="/examples/leaflet/">Leaflet</a></li>
<li><a href="/examples/openlayers/">OpenLayers</a></li>
</ul>

<h2>React</h2>
<ul>
<li><a href="examples/react/">Headless (no map, just the control)</a></li>
<li><a href="/examples/react/">Headless (no map, just the control)</a></li>
</ul>

<h2>Vanilla JS (UMD)</h2>
Expand All @@ -32,7 +32,7 @@ <h2>Vanilla JS (UMD)</h2>
</p>

<ul>
<li><a href="examples/standalone/maplibregl.html">MapLibre GL JS</a></li>
<li><a href="examples/standalone/leaflet.html">Leaflet</a></li>
<li><a href="examples/standalone/ol.html">OpenLayers</a></li>
<li><a href="/examples/standalone/maplibregl.html">MapLibre GL JS</a></li>
<li><a href="/examples/standalone/leaflet.html">Leaflet</a></li>
<li><a href="/examples/standalone/ol.html">OpenLayers</a></li>
</ul>
1,255 changes: 473 additions & 782 deletions package-lock.json

Large diffs are not rendered by default.

42 changes: 22 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@maptiler/geocoding-control",
"version": "1.3.3",
"version": "1.4.0",
"description": "The Javascript & TypeScript Map Control component for MapTiler Geocoding service. Easy to be integrated into any JavaScript mapping application.",
"type": "module",
"author": {
Expand Down Expand Up @@ -102,43 +102,45 @@
"./types": "./types.d.ts"
},
"devDependencies": {
"@maptiler/sdk": "^2.2.2",
"@sveltejs/package": "^2.3.4",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@maptiler/sdk": "^2.3.0",
"@sveltejs/package": "^2.3.5",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@tsconfig/svelte": "^5.0.4",
"@turf/buffer": "^7.1.0",
"@turf/bbox": "^7.1.0",
"@turf/clone": "^7.1.0",
"@turf/difference": "^7.1.0",
"@turf/flatten": "^7.1.0",
"@turf/union": "^7.1.0",
"@types/geojson": "^7946.0.14",
"@types/leaflet": "^1.9.12",
"@types/node": "^22.3.0",
"@types/react": "^18.3.3",
"@types/node": "^22.5.5",
"@types/react": "^18.3.6",
"@types/react-dom": "^18.3.0",
"concurrently": "^8.2.2",
"concurrently": "^9.0.1",
"dotenv": "^16.4.5",
"eslint": "^8.57.0",
"eslint-plugin-svelte": "^2.43.0",
"eslint-plugin-svelte": "^2.44.0",
"esm-env": "^1.0.0",
"globals": "^15.9.0",
"husky": "^9.1.4",
"husky": "^9.1.6",
"leaflet": "^1.9.4",
"lint-staged": "^15.2.9",
"maplibre-gl": "^4.5.2",
"ol": "10.0",
"lint-staged": "^15.2.10",
"maplibre-gl": "^4.7.0",
"ol": "10.1",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.0.0",
"prettier-plugin-svelte": "^3.2.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"replace-in-file": "^8.1.0",
"sass": "^1.77.8",
"svelte": "^4.2.18",
"svelte-check": "^3.8.5",
"sass": "^1.78.0",
"svelte": "^4.2.19",
"svelte-check": "^4.0.2",
"svelte-preprocess": "^6.0.2",
"tslib": "^2.6.3",
"typescript": "^5.5.4",
"typescript-eslint": "^8.1.0",
"vite": "^5.4.0"
"tslib": "^2.7.0",
"typescript": "^5.6.2",
"typescript-eslint": "^8.5.0",
"vite": "^5.4.5"
},
"peerDependencies": {
"@maptiler/sdk": "^1 || ^2",
Expand Down
9 changes: 8 additions & 1 deletion src/geoUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ export function unwrapBbox(bbox0: BBox): BBox {
const bbox = [...bbox0] satisfies BBox;

if (bbox[2] < bbox[0]) {
bbox[2] += 360;
if (
Math.abs((bbox[0] + bbox[2] + 360) / 2) >
Math.abs((bbox[0] - 360 + bbox[2]) / 2)
) {
bbox[0] -= 360;
} else {
bbox[2] += 360;
}
}

return bbox;
Expand Down
58 changes: 57 additions & 1 deletion src/leaflet-controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import bbox from "@turf/bbox";
import clone from "@turf/clone";
import { feature, featureCollection } from "@turf/helpers";
import union from "@turf/union";
import type {
FeatureCollection,
GeoJSON,
Position as GPosition,
LineString,
MultiLineString,
MultiPolygon,
Polygon,
} from "geojson";
import * as L from "leaflet";
import { unwrapBbox } from "./geoUtils";
import MarkerIcon from "./MarkerIcon.svelte";
import { setMask } from "./mask";
import type { BBox, Feature, MapController, MapEvent, Position } from "./types";
Expand Down Expand Up @@ -207,7 +212,31 @@ export function createLeafletMapController(
picked.geometry.type === "Polygon" ||
picked.geometry.type === "MultiPolygon"
) {
setMask(picked as Feature<Polygon | MultiPolygon>, setData);
setMask(picked as Feature<Polygon | MultiPolygon>, (fc) => {
if (!fc) {
return;
}

// leaflet doesn't repeat features every 360 degrees along longitude
// so we clone it manually to the direction(s)
// which could be displayed when auto-zoomed on the feature

const features = [...fc.features];

const bb = unwrapBbox(bbox(picked) as BBox);

const span = bb[2] - bb[0];

if (bb[0] - span / 4 < -180) {
features.push(...shiftPolyCollection(fc, -360).features);
}

if (bb[2] + span / 4 > 180) {
features.push(...shiftPolyCollection(fc, 360).features);
}

setData(featureCollection(features));
});
} else if (
picked.geometry.type === "LineString" ||
picked.geometry.type === "MultiLineString"
Expand Down Expand Up @@ -296,3 +325,30 @@ export function createLeafletMapController(
},
} satisfies MapController;
}

function shiftPolyCollection(
featureCollection: FeatureCollection<Polygon | MultiPolygon>,
distance: number,
): FeatureCollection<Polygon | MultiPolygon> {
const cloned = clone(featureCollection);

for (const feature of cloned.features) {
if (feature.geometry.type == "MultiPolygon") {
for (const poly of feature.geometry.coordinates) {
shiftPolyCoords(poly, distance);
}
} else {
shiftPolyCoords(feature.geometry.coordinates, distance);
}
}

return cloned;
}

function shiftPolyCoords(coordinates: GPosition[][], distance: number) {
for (const ring of coordinates) {
for (const position of ring) {
position[0] += distance;
}
}
}
8 changes: 6 additions & 2 deletions src/maplibregl-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function createMapLibreGlMapController(

let reverseMarker: maplibregl.Marker | undefined;

let savedData: GeoJSON; // used to restore features on style switch
let savedData: GeoJSON | undefined; // used to restore features on style switch

function addFullGeometryLayer() {
if (
Expand Down Expand Up @@ -142,9 +142,13 @@ export function createMapLibreGlMapController(
return new maplibregl.Marker({ element, offset: [1, -13] });
}

function setData(data: GeoJSON) {
function setData(data?: GeoJSON) {
savedData = data;

if (!data) {
return;
}

(map.getSource("full-geom") as GeoJSONSource)?.setData(data);
}

Expand Down
74 changes: 41 additions & 33 deletions src/mask.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
import buffer from "@turf/buffer";
import bbox from "@turf/bbox";
import difference from "@turf/difference";
import flatten from "@turf/flatten";
import { featureCollection, polygon } from "@turf/helpers";
import union from "@turf/union";
import type {
Feature,
FeatureCollection,
MultiPolygon,
Polygon,
Position,
} from "geojson";

// see https://maplibre.org/maplibre-gl-js-docs/example/line-across-180th-meridian/
function fixRing(ring: Position[]) {
let prev: Position | undefined = undefined;

for (const c of ring) {
if (prev && c[0] - prev[0] >= 180) {
c[0] -= 360;
} else if (prev && c[0] - prev[0] < -180) {
c[0] += 360;
}

prev = c;
}
}
import { unwrapBbox } from "./geoUtils";
import type { BBox } from "./types";

export function setMask(
picked: Feature<Polygon | MultiPolygon>,
setData: (data: FeatureCollection<Polygon | MultiPolygon>) => void,
) {
setData: (data?: FeatureCollection<Polygon | MultiPolygon>) => void,
): void {
const diff = difference(
featureCollection([
polygon([
Expand All @@ -47,25 +35,45 @@ export function setMask(
return;
}

diff.properties = { isMask: "y" };
diff.properties = { isMask: true };

const fixed = buffer(picked, 0);
const bb = unwrapBbox(bbox(picked) as BBox);

if (!fixed) {
return;
}
// bigger features (continents, oceans) have bigger tolerance
// because of the used source data simplification
const tolerance = (bb[2] - bb[0]) / 360 / 1_000;

if (fixed.geometry.type === "Polygon") {
for (const ring of fixed.geometry.coordinates) {
fixRing(ring);
}
} else {
for (const poly of fixed.geometry.coordinates) {
for (const ring of poly) {
fixRing(ring);
const leaksLeft = bb[0] < -180;
const leaksRight = bb[2] > 180;

const flattened = flatten(picked);

if (flattened.features.length > 1 && (leaksLeft || leaksRight)) {
for (const poly of flattened.features) {
const bb = unwrapBbox(bbox(poly) as BBox);

if (leaksRight && bb[0] < -180 + tolerance) {
for (const ring of poly.geometry.coordinates) {
for (const position of ring) {
position[0] += 360 - tolerance;
}
}
}

if (leaksLeft && bb[2] > 180 - tolerance) {
for (const ring of poly.geometry.coordinates) {
for (const position of ring) {
position[0] -= 360 - tolerance;
}
}
}
}
}

setData(featureCollection([fixed, diff]));
setData(
featureCollection([
flattened.features.length < 2 ? picked : (union(flattened) ?? picked),
diff,
]),
);
}
Loading

0 comments on commit 401ae0f

Please sign in to comment.