Add nominatim search to maputnik (#873)

This replaces PR:
- #785

Before:

![image](https://github.com/maplibre/maputnik/assets/3269297/95297211-4108-43d8-8a43-42f87e2fbe16)
After:

![image](https://github.com/maplibre/maputnik/assets/3269297/86c3ae58-1bb7-4d2c-8ad9-6b84a21c96f7)

This is based on the geocoder example in maplibre docs:
https://maplibre.org/maplibre-gl-js/docs/examples/geocoder/
This commit is contained in:
Harel M
2024-02-04 11:37:23 +02:00
committed by GitHub
parent ea42f434eb
commit cb77c6b4e2
5 changed files with 103 additions and 3 deletions

View File

@@ -23,4 +23,10 @@ describe("map", () => {
); );
}); });
}); });
describe("search", () => {
it('should exist', () => {
then(get.searchControl()).shouldBeVisible();
});
});
}); });

View File

@@ -177,5 +177,6 @@ export class MaputnikDriver {
skipTargetLayerEditor: () => skipTargetLayerEditor: () =>
this.helper.get.elementByTestId("skip-target-layer-editor"), this.helper.get.elementByTestId("skip-target-layer-editor"),
canvas: () => this.helper.get.element("canvas"), canvas: () => this.helper.get.element("canvas"),
searchControl: () => this.helper.get.element('.maplibregl-ctrl-geocoder')
}; };
} }

52
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@mapbox/mapbox-gl-rtl-text": "^0.2.3", "@mapbox/mapbox-gl-rtl-text": "^0.2.3",
"@maplibre/maplibre-gl-geocoder": "^1.5.0",
"@maplibre/maplibre-gl-style-spec": "^20.1.0", "@maplibre/maplibre-gl-style-spec": "^20.1.0",
"@mdi/js": "^6.6.96", "@mdi/js": "^6.6.96",
"@mdi/react": "^1.5.0", "@mdi/react": "^1.5.0",
@@ -2572,6 +2573,23 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@maplibre/maplibre-gl-geocoder": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-geocoder/-/maplibre-gl-geocoder-1.5.0.tgz",
"integrity": "sha512-PsAbV7WFIOu5QYZne95FiXoV7AV1/6ULMjQxgInhZ5DdB0hDLjciQPegnyDgkzI8JfeqoUMZVS/MglZnSZYhyQ==",
"dependencies": {
"lodash.debounce": "^4.0.6",
"subtag": "^0.5.0",
"suggestions-list": "^0.0.2",
"xtend": "^4.0.1"
},
"engines": {
"node": ">=6"
},
"peerDependencies": {
"maplibre-gl": ">=1.14.0"
}
},
"node_modules/@maplibre/maplibre-gl-style-spec": { "node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "20.1.0", "version": "20.1.0",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.1.0.tgz", "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.1.0.tgz",
@@ -7229,6 +7247,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/fuzzy": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz",
"integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -9211,9 +9237,7 @@
"node_modules/lodash.debounce": { "node_modules/lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
"dev": true,
"peer": true
}, },
"node_modules/lodash.flattendeep": { "node_modules/lodash.flattendeep": {
"version": "4.4.0", "version": "4.4.0",
@@ -12756,6 +12780,20 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/subtag": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/subtag/-/subtag-0.5.0.tgz",
"integrity": "sha512-CaIBcTSb/nyk4xiiSOtZYz1B+F12ZxW8NEp54CdT+84vmh/h4sUnHGC6+KQXUfED8u22PQjCYWfZny8d2ELXwg=="
},
"node_modules/suggestions-list": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/suggestions-list/-/suggestions-list-0.0.2.tgz",
"integrity": "sha512-Yw0fdq14c6RQWQIfE1/8WEi9Dp8rjyCD6FhYA/Tit2/ADbE9Y4ADG4ezlvivsa8Civ5nz++pyVVBMjOMlgIUJw==",
"dependencies": {
"fuzzy": "^0.1.1",
"xtend": "^4.0.0"
}
},
"node_modules/supercluster": { "node_modules/supercluster": {
"version": "7.1.5", "version": "7.1.5",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz",
@@ -14417,6 +14455,14 @@
"resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz",
"integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==" "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw=="
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@@ -21,6 +21,7 @@
"homepage": "https://github.com/maplibre/maputnik#readme", "homepage": "https://github.com/maplibre/maputnik#readme",
"dependencies": { "dependencies": {
"@mapbox/mapbox-gl-rtl-text": "^0.2.3", "@mapbox/mapbox-gl-rtl-text": "^0.2.3",
"@maplibre/maplibre-gl-geocoder": "^1.5.0",
"@maplibre/maplibre-gl-style-spec": "^20.1.0", "@maplibre/maplibre-gl-style-spec": "^20.1.0",
"@mdi/js": "^6.6.96", "@mdi/js": "^6.6.96",
"@mdi/react": "^1.5.0", "@mdi/react": "^1.5.0",

View File

@@ -13,6 +13,9 @@ import { HighlightedLayer, colorHighlightedLayer } from '../libs/highlight'
import 'maplibre-gl/dist/maplibre-gl.css' import 'maplibre-gl/dist/maplibre-gl.css'
import '../maplibregl.css' import '../maplibregl.css'
import '../libs/maplibre-rtl' import '../libs/maplibre-rtl'
//@ts-ignore
import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder';
import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container) { function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container) {
ReactDOM.render(popup, mountNode); ReactDOM.render(popup, mountNode);
@@ -160,6 +163,8 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
map.showCollisionBoxes = mapOpts.showCollisionBoxes!; map.showCollisionBoxes = mapOpts.showCollisionBoxes!;
map.showOverdrawInspector = mapOpts.showOverdrawInspector!; map.showOverdrawInspector = mapOpts.showOverdrawInspector!;
this.initGeocoder(map);
const zoomControl = new ZoomControl; const zoomControl = new ZoomControl;
map.addControl(zoomControl, 'top-right'); map.addControl(zoomControl, 'top-right');
@@ -225,6 +230,47 @@ export default class MapMaplibreGl extends React.Component<MapMaplibreGlProps, M
this.props.onLayerSelect(index); this.props.onLayerSelect(index);
} }
initGeocoder(map: Map) {
const geocoderConfig = {
forwardGeocode: async (config:{query: string, limit: number, language: string[]}) => {
const features = [];
try {
const request = `https://nominatim.openstreetmap.org/search?q=${config.query}&format=geojson&polygon_geojson=1&addressdetails=1`;
const response = await fetch(request);
const geojson = await response.json();
for (const feature of geojson.features) {
const center = [
feature.bbox[0] +
(feature.bbox[2] - feature.bbox[0]) / 2,
feature.bbox[1] +
(feature.bbox[3] - feature.bbox[1]) / 2
];
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: center
},
place_name: feature.properties.display_name,
properties: feature.properties,
text: feature.properties.display_name,
place_type: ['place'],
center
};
features.push(point);
}
} catch (e) {
console.error(`Failed to forwardGeocode with error: ${e}`);
}
return {
features
};
}
};
const geocoder = new MaplibreGeocoder(geocoderConfig, {maplibregl: MapLibreGl});
map.addControl(geocoder, 'top-left');
}
render() { render() {
return <div return <div
className="maputnik-map__map" className="maputnik-map__map"