Skip to content

Commit

Permalink
added zooming by feature type (#45)
Browse files Browse the repository at this point in the history
* added zooming by feature type

* removed use of experimental `kind` property

* mentioned opelayers
  • Loading branch information
zdila authored Sep 17, 2024
1 parent 401ae0f commit 29ff009
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 44 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@

## About

A _Geocoding control_ for [MapTiler
SDK](https://github.com/maptiler/maptiler-sdk-js), [MapLibre GL
JS](https://github.com/maplibre/maplibre-gl-js) and
[Leaflet](https://leafletjs.com) utilizes [MapTiler Cloud Geocoding
A _Geocoding control_ for [MapTiler SDK](https://github.com/maptiler/maptiler-sdk-js),
[MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js),
[Leaflet](https://leafletjs.com) and [OpenLayers](https://openlayers.org) utilizes [MapTiler Cloud Geocoding
API](https://www.maptiler.com/cloud/geocoding/). With this control, users of
mapping application can find any place on Earth (States, Cities, Streets, Addresses, POIs, ...) down
to the address level, restrict the search area to a specific country, highlight
Expand Down
11 changes: 6 additions & 5 deletions src/FeatureItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@
} while (index > -1 && (!imageUrl || missingIconsCache.has(imageUrl)));
}
$: placeType = feature.id.startsWith("poi.")
? feature.properties?.categories?.join(", ")
: (feature.properties?.place_type_name?.[0] ?? feature.place_type[0]);
$: placeType =
feature.properties?.categories?.join(", ") ??
feature.properties?.place_type_name?.[0] ??
feature.place_type[0];
function handleImgError() {
if (imageUrl) {
Expand All @@ -57,7 +58,7 @@
<img src={imageUrl} alt={category} on:error={() => handleImgError()} />
{:else if feature.address}
<img src={iconsBaseUrl + "housenumber.svg"} alt={placeType} />
{:else if feature.properties?.kind === "road" || feature.properties?.kind === "road_relation"}
{:else if feature.id.startsWith("road.")}
<img src={iconsBaseUrl + "road.svg"} alt={placeType} />
{:else if feature.id.startsWith("address.")}
<img src={iconsBaseUrl + "street.svg"} alt={placeType} />
Expand All @@ -77,7 +78,7 @@
{isReverse ? feature.place_name : feature.place_name.replace(/,.*/, "")}
</span>

{#if showPlaceType === "always" || (showPlaceType && !feature.address && feature.properties?.kind !== "road" && feature.properties?.kind !== "road_relation" && !feature.id.startsWith("address.") && !feature.id.startsWith("postal_code.") && (!feature.id.startsWith("poi.") || !imageUrl) && !isReverse)}
{#if showPlaceType === "always" || (showPlaceType && !feature.address && !feature.id.startsWith("road.") && !feature.id.startsWith("address.") && !feature.id.startsWith("postal_code.") && (!feature.id.startsWith("poi.") || !imageUrl) && !isReverse)}
<span class="secondary">
{placeType}
</span>
Expand Down
106 changes: 89 additions & 17 deletions src/GeocodingControl.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,32 @@
ProximityRule,
} from "./types";
export const ZOOM_DEFAULTS: Record<string, number> = {
continental_marine: 4,
country: 4,
major_landform: 8,
region: 5,
subregion: 6,
county: 7,
joint_municipality: 8,
joint_submunicipality: 9,
municipality: 10,
municipal_district: 11,
locality: 12,
neighbourhood: 13,
place: 14,
postal_code: 14,
road: 16,
poi: 17,
address: 18,
"poi.peak": 15,
"poi.shop": 18,
"poi.cafe": 18,
"poi.restaurant": 18,
"poi.aerodrome": 13,
// TODO add many more
};
let className: string | undefined = undefined;
export { className as class };
Expand Down Expand Up @@ -86,9 +112,9 @@
export let excludeTypes = false;
export let zoom = 16;
export let zoom: number | Record<string, number> = ZOOM_DEFAULTS;
export let maxZoom = 18;
export let maxZoom: number | undefined = undefined;
export let apiUrl: string = import.meta.env.VITE_API_URL;
Expand Down Expand Up @@ -187,14 +213,13 @@
!picked.bbox ||
(picked.bbox[0] === picked.bbox[2] && picked.bbox[1] === picked.bbox[3])
) {
mapController.flyTo(
picked.center,
picked.id.startsWith("poi.") || picked.id.startsWith("address.")
? maxZoom
: zoom,
);
mapController.flyTo(picked.center, computeZoom(picked));
} else {
mapController.fitBounds(unwrapBbox(picked.bbox), 50, maxZoom);
mapController.fitBounds(
unwrapBbox(picked.bbox),
50,
computeZoom(picked),
);
}
listFeatures = undefined;
Expand All @@ -206,12 +231,7 @@
}
$: if (mapController && selected && flyTo && flyToSelected) {
mapController.flyTo(
selected.center,
selected.id.startsWith("poi.") || selected.id.startsWith("address.")
? maxZoom
: zoom,
);
mapController.flyTo(selected.center, computeZoom(selected));
}
// if markerOnSelected was dynamically changed to false
Expand Down Expand Up @@ -563,7 +583,19 @@
const fuzzyOnly = !markedFeatures.some((feature) => !feature.matching_text);
let allZoom: number | undefined;
for (const feature of markedFeatures) {
const featZoom = computeZoom(feature);
allZoom =
maxZoom ??
(allZoom === undefined
? featZoom
: featZoom === undefined
? allZoom
: Math.max(allZoom, featZoom));
if (fuzzyOnly || !feature.matching_text) {
for (const i of [0, 1, 2, 3] as const) {
bbox[i] = Math[i < 2 ? "min" : "max"](
Expand All @@ -576,13 +608,44 @@
if (mapController && markedFeatures.length > 0) {
if (picked && bbox[0] === bbox[2] && bbox[1] === bbox[3]) {
mapController.flyTo(picked.center, zoom);
mapController.flyTo(picked.center, computeZoom(picked));
} else {
mapController.fitBounds(unwrapBbox(bbox), 50, maxZoom);
mapController.fitBounds(unwrapBbox(bbox), 50, allZoom);
}
}
}
function computeZoom(feature: Feature): number | undefined {
if (
!feature.bbox ||
(feature.bbox[0] !== feature.bbox[2] &&
feature.bbox[1] !== feature.bbox[3])
) {
return undefined;
}
if (typeof zoom === "number") {
return feature.id.startsWith("poi.") || feature.id.startsWith("address.")
? maxZoom
: zoom;
}
const index = feature.id.replace(/\..*/, "");
return (
(Array.isArray(feature.properties?.categories)
? (feature.properties.categories as string[]).reduce(
(a, category) => {
const b = zoom[index + "." + category];
return a === undefined ? b : b === undefined ? a : Math.max(a, b);
},
undefined as undefined | number,
)
: undefined) ?? zoom[index]
);
}
function handleReverse(coordinates: [lng: number, lat: number]) {
reverseActive = enableReverse === "always";
Expand Down Expand Up @@ -781,13 +844,22 @@
&.can-collapse {
max-width: 29px;
& input::placeholder {
transition: opacity 0.25s;
opacity: 0;
}
}
&,
&:focus-within,
&:hover {
width: 270px;
max-width: 270px;
& input::placeholder {
opacity: 1;
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/leaflet-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,22 @@ export function createLeafletMapController(
}
},

flyTo(center: Position, zoom: number) {
flyTo(center: Position, zoom?: number) {
map.flyTo([center[1], center[0]], zoom, { duration: 2, ...flyToOptions });
},

fitBounds(bbox: BBox, padding: number, maxZoom: number): void {
fitBounds(bbox: BBox, padding: number, maxZoom?: number): void {
map.flyToBounds(
[
[bbox[1], bbox[0]],
[bbox[3], bbox[2]],
],
{ padding: [padding, padding], duration: 2, maxZoom, ...flyToBounds },
{
padding: [padding, padding],
duration: 2,
...(maxZoom ? { maxZoom } : {}),
...flyToBounds,
},
);
},

Expand Down
8 changes: 4 additions & 4 deletions src/maplibregl-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,17 +165,17 @@ export function createMapLibreGlMapController(
}
},

flyTo(center: Position, zoom: number): void {
map.flyTo({ center, zoom, ...flyToOptions });
flyTo(center: Position, zoom?: number): void {
map.flyTo({ center, ...(zoom ? { zoom } : {}), ...flyToOptions });
},

fitBounds(bbox: BBox, padding: number, maxZoom: number): void {
fitBounds(bbox: BBox, padding: number, maxZoom?: number): void {
map.fitBounds(
[
[bbox[0], bbox[1]],
[bbox[2], bbox[3]],
],
{ padding, maxZoom, ...fitBoundsOptions },
{ padding, ...(maxZoom ? { maxZoom } : {}), ...fitBoundsOptions },
);
},

Expand Down
4 changes: 2 additions & 2 deletions src/openlayers-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export function createOpenLayersMapController(
flyTo(center: Position, zoom: number) {
map.getView().animate({
center: fromLonLat(center, getProjection()),
zoom,
...(zoom ? { zoom } : {}),
duration: 2000,
...flyToOptions,
});
Expand All @@ -206,7 +206,7 @@ export function createOpenLayersMapController(
fitBounds(bbox: BBox, padding: number, maxZoom: number): void {
map.getView().fit(transformExtent(bbox, EPSG_4326, getProjection()), {
padding: [padding, padding, padding, padding],
maxZoom,
...(maxZoom ? { maxZoom } : {}),
duration: 2000,
...flyToBounds,
});
Expand Down
24 changes: 15 additions & 9 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export type MapEvent =
export type MapController = {
setEventHandler(handler: undefined | ((e: MapEvent) => void)): void;

flyTo(center: Position, zoom: number): void;
flyTo(center: Position, zoom?: number): void;

fitBounds(bbox: BBox, padding: number, maxZoom: number): void;
fitBounds(bbox: BBox, padding: number, maxZoom?: number): void;

indicateReverse(reverse: boolean): void;

Expand Down Expand Up @@ -159,16 +159,22 @@ export type ControlOptions = {
fuzzyMatch?: boolean;

/**
* On geocoded result what zoom level should the map animate to when a bbox isn't found in the response.
* If a bbox is found the map will fit to the bbox.
* Default value is `16`
* On geocoded result what zoom level should the map animate to when a bbox in the response isn't present or is a point.
* If a bbox is present and not a point then the map will fit to the bbox.
*
* Value can be a number (deprecated) or key-value pairs, where key is a &lt;type&gt; or &lt;type&gt;.&lt;categoy&gt; and value is the zoom level.
*
* Default value is `GeocodingControl.ZOOM_DEFAULTS`.
*/
zoom?: number;
zoom?: number | Record<string, number>;

/**
* On geocoded result what max zoom level should the map animate to when a bbox isn't found in the response. Used for small features.
* If a bbox is found the map will fit to the bbox.
* Default value is `18`.
* On geocoded result what max zoom level should the map animate to when a bbox in the response isn't present or is a point.
* Used for small features.
*
* If a bbox is present and not a point then the map will fit to the bbox.
*
* @deprecated use `zoom` option
*/
maxZoom?: number;

Expand Down

0 comments on commit 29ff009

Please sign in to comment.