Basic Searching
Searching through your MapsIndoors data is an integral part of a great user experience with your maps. Users can look for places to go, or filter what is shown on the map.
Searches work on all MapsIndoors geodata. It is up to you to create a search experience that fits your use case. To aid you in this, there are a range of filters you can apply to the search queries to get the best results. E.g. you can filter by Categories, search only a specific part of the map or search near a Location.
All three return a list of Locations from your Solution matching the parameters they are given. The results are ranked upon the 3 following factors:
- If a "near" parameter is set, how close is the origin point to the result?
- How well does the search input text match the text of the result (using the "Levenshtein distance" algorithm)?
- Which kind of geodata is the result (e.g. Buildings are ranked over POIs)?
This means that the first item in the search result list will be the one matching the 3 factors best and so forth.
See the full list of parameters:
Parameter | Description | Class |
---|---|---|
take | Max number of Locations to get | MPFilter |
Skip | Skip the first number of entries | MPFilter |
categories | A list of Categories to limit the search to | MPFilter |
Parents | A list of Building or Venue IDs to limit the search to | MPFilter |
Types | A list of Types to limit the search to | MPFilter |
Bounds | Limits the result of Locations to a bounding area | MPFilter |
Floor | Limits the result of Locations to be on a specific Floor | MPFilter |
Near | Sorts the list of Locations on which Location is nearest the point given | MPQuery |
Depth | The Depth property makes it possible to get "x" amount of descendants to the given parent. The default for this is 1 (eg. Building > Floor) | MPFilter |
Example of Creating a Search Query
- Android v4
- iOS v4
- iOS v3
- Web v4
If you are looking for documentation on Android SDK v3, please see here.
- Java
- Kotlin
void findRestroom() {
//Here we will create an empty query because we are only interrested in getting locations that match a category. If you want to be more specific here where you can add a query text like "Unisex Restroom"
MPQuery mpQuery = new MPQuery
.Builder()
.build();
List<String> categories = new ArrayList<>();
categories.add("RESTROOMS");
// Init the filter builder and build a filter, the criteria in this case we want maximum 50 restrooms
MPFilter mpFilter = new MPFilter
.Builder()
.setCategories(categories)
.setTake(50)
.build();
MapsIndoors.getLocationsAsync(mpQuery, mpFilter, (locations, error) -> {
//Check if there is an error and iterate through the list to do what you need with the search
});
}
fun findRestroom() {
//Here we will create an empty query because we are only interrested in getting locations that match a category. If you want to be more specific here where you can add a query text like "Unisex Restroom"
val mpQuery = MPQuery.Builder()
.build()
val categories: MutableList<String> = ArrayList()
categories.add("RESTROOMS")
// Init the filter builder and build a filter, the criteria in this case we want maximum 50 restrooms
val mpFilter = MPFilter.Builder()
.setCategories(categories)
.setTake(50)
.build()
MapsIndoors.getLocationsAsync(mpQuery, mpFilter) { locations: List<MPLocation?>?, error: MIError? ->
//Check if there is an error and iterate through the list to do what you need with the search
}
}
let filter = MPFilter()
let query = MPQuery()
query.query = "Office"
query.near = MPPoint(lat: 57.057964, lon: 9.9504112)
query.take = 1
let locations = await MPMapsIndoors.shared.locationsWith(query: query, filter: filter)
if let location = locations?.first {
// Do something with the first location in the result list
}
let filter = MPFilter.init()
let query = MPQuery.init()
query.query = "Office"
query.near = MPPoint.init(lat: 57.057964, lon: 9.9504112)
query.take = 1
MPLocationService.sharedInstance().getLocationsUsing(query, filter: filter) { (locations, error) in
if let location = locations?.first {
}
}
See the full list of parameters in the reference guide, or below:
const searchParameters = {
q: 'Office',
near: { lat: 38.897579747054046, lng: -77.03658652944773 }, // // Blue Room, The White House
take: 1
}
mapsindoors.services.LocationsService.getLocations(searchParameters).then(locations => {
console.log(locations);
});
Display Search Results on the Map
When displaying the search results it is helpful to filter the map to only show matching Locations. Matching Buildings and Venues will still be shown on the map, as they give context to the user, even if they aren't selectable on the map.
Example of Filtering the Map to Display Searched Locations on the Map
- Android v4
- iOS v4
- iOS v3
- Web v4
- Java
- Kotlin
MapsIndoors.getLocationsAsync(mpQuery, mpFilter, (locations, error) -> {
if (locations != null && !locations.isEmpty()) {
//Query with the locations from the query result. Use default camera behavior
mMapControl.setFilter(locations, MPFilterBehavior.DEFAULT);
}
});
MapsIndoors.getLocationsAsync(mpQuery, mpFilter, (locations, error) -> {
//Query with the locations from the query result. Use default camera behavior
mMapControl.setFilter(locations, MPFilterBehavior.DEFAULT)
});
let filter = MPFilter()
let query = MPQuery()
query.query = "Office"
query.near = MPPoint(lat: 57.057964, lon: 9.9504112)
query.take = 1
let locations = await MPMapsIndoors.shared.locationsWith(query: query, filter: filter)
myMapControl.setFilter(locations: locations, behavior: .default)
let filter = MPFilter()
let query = MPQuery()
query.query = "Office"
query.near = MPPoint(lat: 57.057964, lon: 9.9504112)
query.take = 1
MPLocationService.sharedInstance().getLocationsUsing(query, filter: filter) { (locations, error) in
myMapControl.searchResult = locations
}
const searchParameters = {
q: 'Office',
near: { lat: 38.897579747054046, lng: -77.03658652944773 }, // // Blue Room, The White House
take: 1
}
mapsindoors.services.LocationsService.getLocations(searchParameters).then(locations => {
mapsIndoorsInstance.filter(locations.map(location => location.id), false);
});
Clearing the Map of Your Filter
After displaying the search results on your map you can then clear the filter so that all Locations show up on the map again.
Example of Clearing Your Map Filter to Show All Locations Again
- Android v4
- iOS v4
- iOS v3
- Web v4
- Java
- Kotlin
mMapControl.clearFilter();
mMapControl.clearFilter()
myMapControl.clearFilter()
myMapControl.clearMap()
mapsIndoorsInstance.filter(null);
Display Locations as List
You can also search for Locations, and have them presented to you as a list, instead of just displaying them on the map.
The full code example is shown in the JSFiddle below, which will be examined below.
Search
The mapsindoors.services.LocationsService
class exposes the getLocations
function that enables you to search for Locations.
It will return a Promise that gets resolved when the query has executed.
See mapsindoors.services.LocationsService for more information.
searchElement.addEventListener('input', debounce((e) => {
const value = e.target.value;
if (value > '') {
mapsindoors.services.LocationsService.getLocations({ q: value, includeOutsidePOI: true })
.then(displayResults)
.then(filterMap);
} else {
clearResults();
clearFilter();
}
}, 500));
The debounce
method is there to ensure that the service is not being called in rapid succession.
This method delays the execution of the function by 500ms, unless debounce
is called again within 500ms, in which case the timer is reset.
See this article "What is debouncing" by Jamis Charles for a more detailed description of the debounce
concept.
When the function executes, we check whether the input is empty or not. A request object is created if the input is not empty.
The getLocations
function expects either no input, in which case it returns all Locations, or an Object (please refer to the official documentation for an exhaustive list of properties).
In this case, the constant value
is passed to the q
property and the includeOutsidePOI
property is set to true
. When the Promise resolves, the response is passed to the displayResults
helper function.
If the input is empty, we clear the result list and reset the map filter by calling the helper functions clearResults
and clearFilter
.
Checking for Results
We need to clear the previous results, and check if any Locations were returned. If so, we loop through them and add them to the result list.
function displayResults(locations) {
clearResults();
if (locations.length > 0) {
for (const location of locations) {
searchResults.innerHTML += `<li>${location.properties.name}</li>`;
}
} else {
searchResults.innerHTML = '<li class="no-results">No results matched the query.</li>';
}
return locations;
}
If no Locations are returned, a message is shown to the user stating "No results matched the query.". Otherwise, we pass the Locations on to the next helper function called filterMap
.
function filterMap(locations) {
mapsIndoors.filter(locations.map(location => location.id), false);
return locations;
}
The purpose of the filterMap
function is to create a list of location id
s used to filter the Locations on the map.
The second parameter tells MapsIndoors not to change the viewport of the map.
For more information, see MapsIndoors.filter
in the reference documentation.