Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map template: fit bounds when selecting category #438

Merged
6 changes: 6 additions & 0 deletions packages/map-template/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.66.0] - 2025-01-16

## Added

- The map now fits to matching Locations when the user clicks/taps on a Category.

## [1.65.3] - 2025-01-15

## Fixed
Expand Down
51 changes: 48 additions & 3 deletions packages/map-template/src/components/Search/Search.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function Search({ onSetSize, isOpen }) {
window.mapsindoors.services.LocationsService.getLocations({
categories: category,
venue: searchAllVenues ? undefined : venuesInSolution.find(venue => venue.name.toLowerCase() === currentVenueName.toLowerCase())?.name,
}).then(onResults);
}).then(results => onResults(results, true));
}

/**
Expand All @@ -136,15 +136,20 @@ function Search({ onSetSize, isOpen }) {
/**
* Handle search results from the search field.
*
* @param {array} locations
* @param {array} locations - An array of MapsIndoors Location objects.
* @param {boolean} fitMapBounds - If the map bounds should be adjusted to fit the locations.
*/
function onResults(locations) {
function onResults(locations, fitMapBounds = false) {
const displayResults = locations.slice(0, MAX_RESULTS);

setSearchResults(displayResults);
setFilteredLocations(displayResults);
setShowNotFoundMessage(displayResults.length === 0);

if (locations && fitMapBounds) {
fitMapBoundsToLocations(locations);
}

// Handles updates to scroll buttons when the category changes.
// When a category changes, the scroll buttons need to have their enabled/disabled states updated.
// Since some categories might load before the DOM element is fully rendered, we listen for the 'transitionend' event.
Expand All @@ -157,6 +162,46 @@ function Search({ onSetSize, isOpen }) {
}
}


/**
* Adjusts the map view to fit the bounds of the provided locations.
* It will filter out Locations that are not on the current floor or not part of the current venue.
*
* @param {Array} locations - An array of Location objects to fit within the map bounds.
*/
function fitMapBoundsToLocations(locations) {
if (!mapsIndoorsInstance.goTo) return; // Early exit to prevent crashes if using an older version of the MapsIndoors JS SDK. The goTo method was introduced in version 4.38.0.

const currentFloorIndex = mapsIndoorsInstance.getFloor();

// Create a GeoJSON FeatureCollection from the locations that can be used as input to the goTo method.
const featureCollection = {
type: 'FeatureCollection',
features: locations
// Filter out locations that are not on the current floor. If those were included, it could result in a wrong fit since they are not visible on the map anyway.
.filter(location => parseInt(location.properties.floor, 10) === parseInt(currentFloorIndex, 10))

// Filter out locations that are not part of the current venue. Including those when fitting to bounds could cause the map to zoom out too much.
.filter(location => location.properties.venueId.toLowerCase() === currentVenueName.toLowerCase())

// Map the locations to GeoJSON features.
.map(location => ({
type: 'Feature',
geometry: location.geometry,
properties: location.properties
}))
};

if (featureCollection.features.length > 0) {
Promise.all([getBottomPadding(), getLeftPadding()]).then(([bottomPadding, leftPadding]) => {
mapsIndoorsInstance.goTo(featureCollection, {
maxZoom: 22,
padding: { bottom: bottomPadding, left: leftPadding, top: 0, right: 0 }
});
});
}
}

/**
* Clear results list when search field is cleared.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/map-template/src/hooks/useMediaQuery.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useState, useEffect } from "react";
* @param {string} query
*/
const useMediaQuery = (query) => {
const [matches, setMatches] = useState(false);
const [matches, setMatches] = useState(window.matchMedia(query).matches);

useEffect(() => {
const media = window.matchMedia(query);
Expand Down
Loading