The high-level API of the Location Library provides a way to instantiate various kinds of path matchers and a number of factory methods to help you directly implement your algorithms using the following interfaces:
ProximitySearch To find topology segments (vertices) from coordinates.
DirectedGraph To navigate the topology with an efficient data structure.
PropertyMap To access road, navigation, advanced navigation, and ADAS attributes.
To resolve sequences of recorded geographical coordinates to paths on the road topology graph, you can use a path matcher. The path matcher will return information about the position on the road that most likely corresponds to each input point, and the most likely connection (transition) between points.
Figure 1. Path matching
Note
Dense and sparse traces of probe data
The sequence of recorded geographical coordinates is called trace or probe trace. The path matchers distinguish between dense and sparse traces. Dense traces are characterized by distances between consecutive probes that are short compared to lengths of the road segments in the map. Typically, probes collected every 1 to 5 seconds are considered dense.
Include the following imports to use the PathMatchers factory:
In order to create path matchers, you can use various methods:
The unrestrictedPathMatcher creates a map matcher that produces results independent from any driving restriction, and is suitable for dense GPS traces (at least one point every 5 seconds).
Scala
Java
val unrestrictedPathMatcher: PathMatcher[GeoCoordinate, Vertex, NoTransition]=
PathMatchers.unrestrictedPathMatcher(optimizedMap, cacheManager)
The unrestrictedPathMatcher is the most effective method to match to the most probably driven path for dense probes, and all vehicle types. You will not necessarily be able to follow this path by driving, but you can use the results to derive warnings about driving errors.
The unrestrictedPathMatcher may introduce disconnections when consecutive points are not on directly connected topology segments, as it does not use any routing algorithm to compute the transitions.
The unrestrictedPathMatcherWithTransitions variant will make a bigger effort to reconnect points with a simple shortest path algorithm.
The carPathMatcherWithTransitions takes most car driving restrictions into consideration, and is a path matcher specifically configured for cars and sparse data, but will also work on dense data. For example, this path matcher will only match input points to vertices that are accessible by cars according to the roadAccess property.
The carPathMatcherWithTransitions works better than the unrestrictedPathMatcherWithTransitions for sparse data because it will route between points using a shortest path algorithm that takes into consideration road access and turn restrictions. In some situations, when consecutive points are too far apart (more than about 30 kilometers of on-road distance), they could be considered unreachable and one of them could be matched to Unknown.
Scala
Java
val carPathMatcherWithTransitions: PathMatcher[GeoCoordinate, Vertex, Seq[Vertex]]=
PathMatchers.carPathMatcherWithTransitions(optimizedMap, cacheManager)
The carPathMatcher variant is similar but is only suitable for dense data, where the distances between consecutive probes are short compared to vertex lengths in the map.
Scala
Java
val carPathMatcher: PathMatcher[GeoCoordinate, Vertex, NoTransition]=
PathMatchers.carPathMatcher(optimizedMap, cacheManager)
In order to associate places in the real world with vertices in the routing graph, ProximitySearch allows you to search for vertices within a certain distance from a point on the map.
Retrieving the closest point is a simple but fast technique to perform map matching. The following code snippet shows how to retrieve information about the closest result:
Scala
Java
val closest = vertices.toSeq.minBy(_.distanceInMeters)
println(s"The closest point is ${closest.nearest} "+s"on vertex ${closest.element} "+s"${closest.fraction *100}% along its length. "+s"It is ${closest.distanceInMeters}m away from $brandenburgerTor.")
finalElementProjection<Vertex> closest =StreamSupport.stream(vertices.spliterator(),false).min(Comparator.comparingDouble(ElementProjection::distanceInMeters)).get();System.out.format("The closest point is %s on vertex %s %f%% along its length. ",
closest.nearest(), closest.element(), closest.fraction()*100);System.out.format("It is %fm away from %s.%n", closest.distanceInMeters(), brandenburgerTor);
The HERE Workspace Examples for Java and Scala Developers contains a full example called PointMatcherExample that uses a ProximitySearch to perform map matching. This example is located in location/scala/standalone (location/java/standalone for the version written in Java).
Note
For more accurate map matching, use a fully fledged path matcher.
Multiple Results for Each Vertex
Since the geometries of the vertices are not always rectilinear, in some cases you may want to get one ElementProjection for each segment of the geometry (the segment between consecutive pairs of points). In these cases, the following code snippet will return multiple projections for the same vertex, one for each segment of the line string of the geometry:
Scala
Java
importcom.here.platform.location.core.geospatial.GeoCoordinate
val hairpinTurn = GeoCoordinate(46.55091,11.81564)val geometrySegmentProximitySearch: ProximitySearch[Vertex]=
ProximitySearches.vertexGeometrySegments(optimizedMap, cacheManager)val closestProjections: Iterable[ElementProjection[Vertex]]=
geometrySegmentProximitySearch.search(hairpinTurn,50/* meters */)
println(s"The projections on the geometry segments within 50 meters of")
println(hairpinTurn)
println("are:")
closestProjections.foreach(println)
finalGeoCoordinate hairpinTurn =newGeoCoordinate(46.55091,11.81564);finalProximitySearch<GeoCoordinate,Vertex> geometrySegmentProximitySearch =ProximitySearches.vertexGeometrySegments(optimizedMap, cacheManager);finalIterable<ElementProjection<Vertex>> closestProjections =
geometrySegmentProximitySearch.search(hairpinTurn,50/* meters */);System.out.format("The projections on the geometry segments within 50 meters of %s %n", hairpinTurn);System.out.println("are:");
closestProjections.forEach(System.out::println);
Note
The following image shows the results from the ProximitySearches.vertices. The search only returns the closest point for each vertex:
By using the ProximitySearches.vertexGeometrySegments variant, you have the option to get more than one projection for each vertex:
Graphs
You can navigate the road topology and geometry of the Here Map Content using a graph abstraction. Each vertex of the graph represents a road (topology segment) in a direction, and each edge a physical connection between roads as explained in the Routing Graph section.
The following code snippet demonstrates how to create a graph that lazily loads data from the routinggraph layer of the Optimized Map for Location Library:
Scala
Java
importcom.here.platform.location.core.graph.DirectedGraph
importcom.here.platform.location.inmemory.graph.{Edge, Vertex}importcom.here.platform.location.integration.optimizedmap.graph.Graphs
val graph: DirectedGraph[Vertex, Edge]= Graphs.from(optimizedMap, cacheManager)
There are always two vertices associated with a road topology segment, one for each physical direction of travel. PropertyMaps can be used to gather attributes to make decisions, for example PropertyMaps.roadAccess described below can be used to derive information about the direction of travel for various modes of transport.
You can obtain a vertex's outgoing edges of a graph and their target vertices as follows:
Scala
Java
val outDegree = graph.outDegree(vertex)val neighbors: Iterable[Vertex]=
graph.outEdgeIterator(vertex).map(graph.target).toIterable
println(s"The $vertex has $outDegree outgoing edges to the following vertices:")
println(neighbors)
finalint outDegree = graph.getOutDegree(vertex);finalList<Vertex> neighbors =newArrayList<>(outDegree);
graph.getOutEdgeIterator(vertex).forEachRemaining(edge -> neighbors.add(graph.getTarget(edge)));System.out.format("The %s has %d outgoing edges to the following vertices:%n", vertex, outDegree);
neighbors.forEach(v ->System.out.format("%s%n", v));
The following example shows how to create a graph to navigate the road topology backwards:
Scala
Java
val reversedGraph: DirectedGraph[Vertex, Edge]= Graphs.reversedFrom(optimizedMap, cacheManager)
Determine whether the turn represented by a given edge is restricted for a particular vehicle type
Scala
Java
importcom.here.platform.location.inmemory.graph.Edge
importcom.here.platform.location.integration.optimizedmap.graph.AccessRestriction
val carTurnRestrictionsMap: PropertyMap[Edge,Boolean]=
PropertyMaps.turnRestrictions(optimizedMap, cacheManager, AccessRestriction.Automobile)val isRestrictedForCars:Boolean= carTurnRestrictionsMap(edge)
importcom.here.platform.location.compilation.heremapcontent.javadsl.AttributeAccessor;importcom.here.platform.location.compilation.heremapcontent.javadsl.AttributeAccessors;importcom.here.platform.location.core.graph.javadsl.RangeBasedProperty;importcom.here.platform.location.core.graph.javadsl.RangeBasedPropertyMap;importcom.here.platform.location.inmemory.graph.Vertex;importcom.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;importcom.here.schema.rib.v2.common_attributes.SpeedLimitAttribute;importcom.here.schema.rib.v2.navigation_attributes_partition.NavigationAttributesPartition;importjava.util.List;// Note: use the factory method that corresponds to the HERE Map Content layer,// for example `navigationAttribute` for compiling the attributes from 'navigation-attributes'// layer:finalAttributeAccessor<NavigationAttributesPartition,Integer> speedLimitAccessor =AttributeAccessors.forHereMapContentSegmentAnchor(NavigationAttributesPartition::speedLimit,SpeedLimitAttribute::value);finalRangeBasedPropertyMap<Vertex,Integer> speedLimitMap =PropertyMaps.navigationAttribute(
optimizedMap,"speed-limit", hereMapContent, cacheManager, speedLimitAccessor);finalList<RangeBasedProperty<Integer>> speedLimits = speedLimitMap.get(vertex);
Retrieve many of the HERE Map Content attributes of a vertex by compiling them on-the-fly. The Location Library supports on-the-fly compilation of segment attributes with the segment anchor types "Point", "Single-Segment" and "Multi-Segment".
If you know that the segment anchors for the attribute in question don't overlap, you can use the following API to compile it.
Scala
Java
// Note: use the factory method that corresponds to the HERE Map Content layer,// for example `navigationAttribute` for compiling the attributes from 'navigation-attributes' layer:importcom.here.platform.location.compilation.heremapcontent.{
AttributeAccessor,
AttributeAccessors
}importcom.here.platform.location.core.graph.RangeBasedProperty
importcom.here.schema.rib.v2.common_attributes.SpeedLimitAttribute
importcom.here.schema.rib.v2.navigation_attributes_partition.NavigationAttributesPartition
val speedLimitAccessor: AttributeAccessor[NavigationAttributesPartition,Int]=
AttributeAccessors
.forHereMapContentSegmentAnchor[NavigationAttributesPartition, SpeedLimitAttribute,Int](
_.speedLimit,
_.value)val speedLimitMap: RangeBasedPropertyMap[Vertex,Int]=
PropertyMaps.navigationAttribute(
optimizedMap,"speed-limit",
hereMapContent,
cacheManager,
speedLimitAccessor
)val speedLimits: Seq[RangeBasedProperty[Int]]= speedLimitMap(vertex)
importcom.here.platform.location.compilation.heremapcontent.javadsl.AttributeAccessor;importcom.here.platform.location.compilation.heremapcontent.javadsl.AttributeAccessors;importcom.here.platform.location.core.graph.javadsl.RangeBasedProperty;importcom.here.platform.location.core.graph.javadsl.RangeBasedPropertyMap;importcom.here.platform.location.inmemory.graph.Vertex;importcom.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;importcom.here.schema.rib.v2.common_attributes.SpeedLimitAttribute;importcom.here.schema.rib.v2.navigation_attributes_partition.NavigationAttributesPartition;importjava.util.List;// Note: use the factory method that corresponds to the HERE Map Content layer,// for example `navigationAttribute` for compiling the attributes from 'navigation-attributes'// layer:finalAttributeAccessor<NavigationAttributesPartition,Integer> speedLimitAccessor =AttributeAccessors.forHereMapContentSegmentAnchor(NavigationAttributesPartition::speedLimit,SpeedLimitAttribute::value);finalRangeBasedPropertyMap<Vertex,Integer> speedLimitMap =PropertyMaps.navigationAttribute(
optimizedMap,"speed-limit", hereMapContent, cacheManager, speedLimitAccessor);finalList<RangeBasedProperty<Integer>> speedLimits = speedLimitMap.get(vertex);
For attributes with overlapping ranges, there is another method you can use:
In the Here Map Content layers some attributes have a unique value for each section of a topology segment (e.g. number of lanes or functional class). Others may have multiple overlapping values (e.g. time dependent attributes that have multiple values in the same spot, that apply during different times). Attributes are "non-overlapping" if they have at most a single value for each location, and they are "overlapping" if they can have multiple different values that apply to the same location.