For non-commercial use only. It has a limited credit count per minute, a limited set of vehicle profiles and cannot use the flexible mode (ch.disable=true).
For the Geocoding API we have integrated different providers. Each provider has its own prices. To take these differences into account, the prices per geocoding request vary depending on the provider.
The following list shows the credit costs per request for the different providers:
- default: 0.3 credits
- gisgraphy: 0.3 credits
- nominatim: 0.9 credits
- nettoolkit: 0.9 credits
- opencagedata: 0.9 credits
2 months free when billed annually.
Some of the datasets we rely on, like OpenStreetMap, tend to have results in local languages. Some others have results only in English. Specifying a language does not guarantee a response purely in that language. We will, however, do our best to favour that language if our data has it.
OSM and HERE Maps comparison on https://tools.geofabrik.de/mc/
In template (here `{{ place }}` rendered by Django, not by Vue):
{# Search term dropdown #}
-
In javascript:
const highlightingEl = { start: '', end: '' };
const autocompletionUrl = 'https://autocomplete.geocoder.api.here.com/6.2/suggest.json';
// Sample of Vue app method
fetchSearchPlaceResults() {
let vm = this;
let params = '?' +
'query=' + encodeURIComponent(vm.place) +
'&beginHighlight=' + encodeURIComponent(highlightingEl.start) +
'&endHighlight=' + encodeURIComponent(highlightingEl.end) +
'&maxresults=15' +
'&language=de' + // Show localized
'&country=DEU,AUT' + // Search only in Germany and Austria
'&app_id=' + hereAppId +
'&app_code=' + hereAppCode;
let request = new XMLHttpRequest();
request.open('GET', autocompletionUrl + params );
request.onload = function(e) {
let data = JSON.parse(request.response);
if (data.suggestions) {
vm.searchPlaceResults = data.suggestions;
vm.showSearchPlaceDropdown(); // Triggers dropdown
} else {
vm.hideSearchPlaceDropdown(); // Triggers dropdown
}
};
request.send();
}
Example of suggestions for Koln.
{
"suggestions": [
{
"label": "Deutschland, Köln, Köln",
"language": "de",
"countryCode": "DEU",
"locationId": "NT_BXqF2OWXjw0Df08cTCVyUB",
"address": {
"country": "Deutschland",
"state": "Nordrhein-Westfalen",
"county": "Köln",
"city": "Köln",
"postalCode": "50667"
},
"matchLevel": "city"
},
{
"label": "Deutschland, Köln",
"language": "de",
"countryCode": "DEU",
"locationId": "NT_40ftPMCK.PEJF3psEkvBNA",
"address": {
"country": "Deutschland",
"state": "Nordrhein-Westfalen",
"county": "Köln"
},
"matchLevel": "county"
}
]
}
Filter results based on the page requirements:
if (data.suggestions) {
if (document.getElementById('searchbar')) {
vm.searchPlaceResults = data.suggestions.filter(function (item) {
return item.matchLevel === 'city';
});
}
else {
vm.searchPlaceResults = data.suggestions;
}
vm.showSearchPlaceDropdown();
}
In template:
{% block scripts %}
{% endblock %}
In javascript:
let markerIcon = new H.map.Icon(markerImage);
Now we use SVG to draw a marker.
const pixelRatio = window.devicePixelRatio || 1;
const markerSize = 40 * pixelRatio;
const markerIcon = new H.map.Icon(markerSVGTemplate, {
size: { w: markerSize, h: markerSize },
anchor: { x: markerSize / 2, y: markerSize / 2 },
});
Our custom info window
.H_ib * {
box-sizing: border-box;
}
.H_ib_body {
box-sizing: border-box;
width: 320px;
background: var(--white);
font-size: 18px;
color: var(--outer-space);
margin-right: 35px;
bottom: 25px;
box-shadow: 3px 3px 10px 0 rgba(0, 0, 0, 0.5);
}
Terrain Map Tile
Traffic tile
Satellite Tile
Normal Day Tile
Setting map style in javascript
// Map initialization
let platform = new H.service.Platform({
'app_id': hereAppId,
'app_code': hereAppCode,
'useHTTPS': true,
});
let defaultLayers = platform.createDefaultLayers();
let map = new H.Map(
document.getElementById(mapElementId),
defaultLayers.normal.map,
mapOptions,
);
// Setting map style
let mapTileService = platform.getMapTileService({
type: 'aerial',
});
let mapLayer = mapTileService.createTileLayer(
'maptile',
'terrain.day',
pixelRatio === 1 ? 256 : 512,
'png8',
{
lg: 'GER',
ppi: pixelRatio === 1 ? undefined : 320,
},
);
map.setBaseLayer(mapLayer);
let strategy = H.clustering.Provider.Strategy.GRID;
// Use FASTGRID strategy only for pages with search results
if (document.getElementById("results")) {
strategy = H.clustering.Provider.Strategy.FASTGRID;
}
let clusteredDataProvider = new H.clustering.Provider(dataPoints, {
clusteringOptions: {
strategy: strategy,
eps: 30,
minWeight: 2
},
});
For 6000+ points with FASTGRID markers/clusters appear on the initialized map much faster.
GRID
FASTGRID
All are closed
One is open
Spiderfier source code on GitHub | Screenshots were made based on this demo
For single marker
For cluster (with arrows to change)
Cluster SVG template:
const clusterSVGTemplate = ``;
Preparing cluster icon for rendering:
// Sample of Vue app method
getClusterIcon(clusterWeight) {
let svgString = clusterSVGTemplate.replace(/{weight}/g, clusterWeight);
return new H.map.Icon(svgString, {
size: {w: markerSize, h: markerSize},
anchor: {x: markerSize / 2, y: markerSize / 2}
});
},
import urllib
from django.conf import settings
from django.core.cache import cache
import requests
HERE_GEOCODE_API_URL = 'https://geocode.search.hereapi.com/v1/geocode'
HERE_REVERSE_GEOCODE_API_URL = 'https://revgeocode.search.hereapi.com/v1/revgeocode'
HERE_LOCATION_API_URL = 'https://lookup.search.hereapi.com/v1/lookup'
class HereClient(object):
def _get_url_params(self, **extra_params):
params = {'apiKey': settings.HERE_API_KEY, 'lang': settings.HERE_LANGUAGE}
params.update(**extra_params)
return urllib.parse.urlencode(params)
def make_request(self, url, **params):
url = '%s?%s' % (url, self._get_url_params(**params))
response = requests.get(url)
if response.status_code == 200:
data = response.json()
return data
def geocode(self, query):
data = self.make_request(HERE_GEOCODE_API_URL, q=query)
if data:
return data['items']
def reverse_geocode(self, latitude, longitude):
params = {'limit': 1, 'at': '%s,%s' % (latitude, longitude)}
data = self.make_request(HERE_REVERSE_GEOCODE_API_URL, **params)
if data:
return data['items']
def lookup(self, location_id):
cached_record = cache.get(location_id, None)
if not cached_record:
data = self.make_request(HERE_LOCATION_API_URL, id=location_id)
cache.set(location_id, data)
return cached_record
return cached_record
Model:
from django.contrib.gis.db import models
class Place(models.Model):
name = models.CharField(max_length=255)
point = models.PointField()
def __str__(self):
return self.name
@property
def lat(self):
return self.point[1]
@property
def lng(self):
return self.point[0]
API view:
from django.contrib.gis.geos import Point
from rest_framework import generics, serializers
from apps.places.models import Place
class PlaceSerializer(serializers.ModelSerializer):
class Meta:
model = Place
fields = ('name', 'lat', 'lng')
class PlaceListAPIView(generics.ListAPIView):
queryset = Place.objects.all()
serializer_class = PlaceSerializer
def filter_queryset(self, queryset):
query = self.request.query_params.get('q', None)
latitude = self.request.query_params.get('lat', None)
longitude = self.request.query_params.get('lng', None)
radius = self.request.query_params.get('r', 15) or 15
if query:
queryset = queryset.filter(name__search=query)
if latitude and longitude:
longitude, latitude = map(float, (longitude, latitude))
geopoint = Point(longitude, latitude)
queryset = queryset.filter(
point__dwithin=(geopoint, radius)
)
return queryset
Haystack search index definition:
from haystack import indexes
from apps.places.models import Place
class PlaceIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, model_attr='name')
name = indexes.CharField(model_attr='name')
point = indexes.LocationField(model_attr='point')
lat = indexes.FloatField(model_attr='lat')
lng = indexes.FloatField(model_attr='lng')
def get_model(self):
return Place
API view:
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from haystack.query import SearchQuerySet
from rest_framework import generics, serializers
class SearchRecordSerializer(serializers.Serializer):
name = serializers.CharField()
lat = serializers.FloatField()
lng = serializers.FloatField()
class SearchResultsAPIView(generics.ListAPIView):
queryset = SearchQuerySet()
serializer_class = PlaceSerializer
def filter_queryset(self, queryset):
query = self.request.query_params.get('q', None)
latitude = self.request.query_params.get('lat', None)
longitude = self.request.query_params.get('lng', None)
radius = self.request.query_params.get('radius', 15) or 15
if query:
queryset = queryset.filter(content=query)
if latitude and longitude:
longitude, latitude = map(float, (longitude, latitude))
geopoint = Point(longitude, latitude)
max_distance = D(km=radius)
queryset = queryset.dwithin(
'location', geopoint, max_distance,
)
return queryset