Skip to main content

Search & Geocoding features

|

The Maps SDK for TypeScript provides geocoding and reverse geocoding capabilities. Key features include:

  • Reverse Geocoding: Transform geographic coordinates into comprehensive address details, such as country, city, street name, postal code, and more.
  • Geocoding: Locate specific places (e.g., cities, streets, or house numbers) based on address components.
  • Route-Based Search: Perform searches along predefined routes to identify landmarks and points of interest.
  • Wikipedia Integration: Access Wikipedia descriptions and related information for identified landmarks.
  • Auto-Suggestion Implementation: Dynamically generate search suggestions as users type.

Reverse geocode coordinates to address

Given a coordinate, we can get the corresponding address by searching around the given location and getting the AddressInfo associated with the nearest Landmark found nearby. The AddressInfo provides information about the country, city, street name, street number, postal code, state, district, country code, and other relevant information.

Fields from an AddressInfo object can be accessed via the getField method or can be automatically converted to a string containing the address info using the format method.

import { SearchService, SearchPreferences, Coordinates, GemError, Landmark, AddressField } from '@magiclane/maps-sdk';

const prefs = SearchPreferences.create({
maxMatches: 10,
searchAddresses: true,
searchMapPOIs: true
});

const coordinates = new Coordinates({
latitude: 51.519305,
longitude: -0.128022,
});

SearchService.searchAroundPosition({
position: coordinates,
preferences: prefs,
onCompleteCallback: (err, results) => {
if (err !== GemError.success || results.length === 0) {
console.log("No results found");
} else {
const landmark = results[0];
const addressInfo = landmark.address;

const country = addressInfo.getField(AddressField.country);
const city = addressInfo.getField(AddressField.city);
const street = addressInfo.getField(AddressField.streetName);
const streetNumber = addressInfo.getField(AddressField.streetNumber);

const fullAddress = addressInfo.format({});
console.log(`Address: ${fullAddress}`);
}
},
});

Geocode address to location

The Maps SDK for TypeScript provides geocoding capabilities to convert addresses into geographic coordinates. Addresses represent a tree-like structure, where each node is a Landmark with a specific AddressDetailLevel. At the root of the tree, we have the country-level landmarks, followed by other levels such as cities, streets, and house numbers.

warning

The address structure is not the same in all countries. For example, some countries do not have states or provinces.

Use the getNextAddressDetailLevel method from the GuidedAddressSearchService class to get the next available levels in the address hierarchy.

Search countries by name

It is possible to do a search at a country level. In this case you can use code like the following:

import { GuidedAddressSearchService, GemError } from '@magiclane/maps-sdk';

GuidedAddressSearchService.searchCountries("Germany", (err, result) => {
if (err !== GemError.success && err !== GemError.reducedResult) {
console.error(`Error: ${err}`);
}

if (result.length === 0) {
console.log("No results");
}

// do something with "result"
});

This can provide the parent landmark for the other GuidedAddressSearchService methods.

It does a search restricted to country-level results. This allows the use of more flexible search terms as it works regardless of the language.

Search the hierarchical address structure

We will create the example in two steps. First, we create a function that for a parent landmark, an AddressDetailLevel and a text returns the children having the required detail level and matching the text.

The possible values for AddressDetailLevel are: noDetail, country, state, county, district, city, settlement, postalCode, street, streetSection, streetLane, streetAlley, houseNumber, crossing.

import { GuidedAddressSearchService, Landmark, AddressDetailLevel, GemError } from '@magiclane/maps-sdk';

// Address search method.
async function searchAddress({
landmark,
detailLevel,
text,
}: {
landmark: Landmark;
detailLevel: AddressDetailLevel;
text: string;
}): Promise<Landmark | null> {
return new Promise((resolve) => {
GuidedAddressSearchService.search(
text,
landmark,
detailLevel,
(err, results) => {
// If there is an error, the method will return an empty list.
if ((err !== GemError.success && err !== GemError.reducedResult) ||
results.length === 0) {
resolve(null);
return;
}

resolve(results[0]);
}
);
});
}

Using the function above, we can look for the children landmarks following the conditions:

const countryLandmark = GuidedAddressSearchService.getCountryLevelItem('ESP');
console.log(`Country: ${countryLandmark?.name}`);

// Use the address search to get a landmark for a city in Spain (e.g., Barcelona).
const cityLandmark = await searchAddress({
landmark: countryLandmark!,
detailLevel: AddressDetailLevel.city,
text: 'Barcelona',
});
if (!cityLandmark) return;
console.log(`City: ${cityLandmark.name}`);

// Use the address search to get a predefined street's landmark in the city (e.g., Carrer de Mallorca).
const streetLandmark = await searchAddress({
landmark: cityLandmark,
detailLevel: AddressDetailLevel.street,
text: 'Carrer de Mallorca',
});
if (!streetLandmark) return;
console.log(`Street: ${streetLandmark.name}`);

// Use the address search to get a predefined house number's landmark on the street (e.g., House Number 401).
const houseNumberLandmark = await searchAddress({
landmark: streetLandmark,
detailLevel: AddressDetailLevel.houseNumber,
text: '401',
});
if (!houseNumberLandmark) return;
console.log(`House number: ${houseNumberLandmark.name}`);

The getCountryLevelItem method returns the root node corresponding to the specified country based on the provided country code. If the country code is invalid, the function will return null. The searchCountries method can also be used.

Geocode location to Wikipedia

A useful functionality when looking for something, is to obtain the content of a Wikipedia page for a specific text filter. This can be done by a normal search, followed by a call to ExternalInfoService.requestWikiInfo.

See the Location Wikipedia guide for more info.

Get auto-suggestions

Auto-suggestions can be implemented by calling SearchService.search with the text filter set to the current text from an input field when the field value is changed. A simple example is demonstrated below:

import { SearchService, Coordinates, SearchPreferences, Landmark, GemError, TaskHandler } from '@magiclane/maps-sdk';

let taskHandler: TaskHandler | null = null;

async function getAutoSuggestion(value: string): Promise<Landmark[]> {
console.log(`New auto suggestion search for ${value}`);

const refCoordinates = new Coordinates({ latitude: 48, longitude: 2 });
const searchPreferences = SearchPreferences.create({
maxMatches: 10,
searchAddresses: true,
searchMapPOIs: true
});

// Cancel previous task.
if (taskHandler !== null) {
SearchService.cancelSearch(taskHandler);
}

// Launch search for new value.
return new Promise((resolve) => {
taskHandler = SearchService.search({
textFilter: value,
referenceCoordinates: refCoordinates,
preferences: searchPreferences,
onCompleteCallback: (error, result) => {
resolve(result);
console.log(`Got result for search ${value}: error - ${error}, result size - ${result.length}`);
},
});
});
}

// Usage example with an input element
const searchInput = document.getElementById('search-input') as HTMLInputElement;
searchInput.addEventListener('input', async (event) => {
const value = (event.target as HTMLInputElement).value;
const suggestions = await getAutoSuggestion(value);

// Display suggestions to the user
displaySuggestions(suggestions);
});

function displaySuggestions(landmarks: Landmark[]) {
// Implementation to display suggestions in the UI
const suggestionsList = document.getElementById('suggestions-list');
if (suggestionsList) {
suggestionsList.innerHTML = '';
landmarks.forEach(landmark => {
const item = document.createElement('li');
item.textContent = landmark.name;
item.addEventListener('click', () => {
// Handle suggestion selection
console.log(`Selected: ${landmark.name}`);
});
suggestionsList.appendChild(item);
});
}
}

The refCoordinates might be replaced with a more suitable value, such as the user current position or the map viewport center.

The suggestion display can be modified to show the icon of the landmark, address and any relevant details depending on the use case.

Search along a route

It is possible also to do a search along a route, not based on some coordinates. In this case you can use code like the following:

import { SearchService, Route, GemError, TaskHandler } from '@magiclane/maps-sdk';

const taskHandler: TaskHandler | null = SearchService.searchAlongRoute({
route,
onCompleteCallback: (err, results) => {
if (err === GemError.success) {
if (results.length === 0) {
console.log("No results");
} else {
console.log(`Results size: ${results.length}`);
for (const landmark of results) {
// do something with landmarks
}
}
} else {
console.log("No results found");
}
},
});
note

SearchPreferences can be passed to customize the search behavior along the route, such as setting maxMatches or limiting distance from the route.