Optimized Map Integration Module

The HERE Map Content format is designed primarily for map compilation. So accessing HERE Map Content directly is not efficient for the main use cases of the Location Library, routing and path-matching.

For optimized access, HERE provides the Optimized Map for Location Library which is a map format compiled from the HERE Map Content specifically for these main use cases.

SBT
Maven
Gradle
libraryDependencies ++= Seq(
  "com.here.platform.location" %% "location-data-loader-standalone" % "0.21.755",
  "com.here.platform.location" %% "location-integration-optimized-map" % "0.21.755"
)
<dependencies>
    <dependency>
        <groupId>com.here.platform.location</groupId>
        <artifactId>location-data-loader-standalone_${scala.compat.version}</artifactId>
        <version>0.21.755</version>
    </dependency>
    <dependency>
        <groupId>com.here.platform.location</groupId>
        <artifactId>location-integration-optimized-map_${scala.compat.version}</artifactId>
        <version>0.21.755</version>
    </dependency>
</dependencies>
dependencies {
    compile group: 'com.here.platform.location', name: 'location-data-loader-standalone_2.12', version:'0.21.755'
    compile group: 'com.here.platform.location', name: 'location-integration-optimized-map_2.12', version:'0.21.755'
}

Note

This chapter demonstrates accessing the Optimized Map for Location Library using the lower-level Component API. Usually, the level of control this API provides is not actually required. The chapter High-Level API shows how to use the simplified Location Library API to access the same information.

Create Catalog Objects

This section describes how to access the catalogs Optimized Map for Location Library and HERE Map Content in your app.

First, you have to create an instance of the CatalogFactory interface. This chapter only describes one implementation of this CatalogFactory interface, StandaloneCatalogFactory:

Scala
import com.here.platform.location.dataloader.standalone.StandaloneCatalogFactory

val catalogFactory = new StandaloneCatalogFactory()

When you are done with manipulating catalogs, terminate the StandaloneCatalogFactory as follows:

Scala
catalogFactory.terminate()

If you don't terminate StandaloneCatalogFactory, your application doesn't terminate.

To create a Catalog object for the Optimized Map for Location Library, use this CatalogFactory and the appropriate catalog HRN and version:

Scala
import com.here.platform.location.dataloader.core.Catalog

val optimizedMap: Catalog = catalogFactory.create(optimizedMapHRN, optimizedMapVersion)

For more information on the definition of the HRN and version of the Optimized Map for Location Library, see the section Catalogs HRNs and versions.

Create TileLoaders

The data in a Catalog is stored in pieces (tiles) in catalog layers. To load data from a particular layer, you need to create a TileLoader for that layer.

You can use the Catalog to create a TileLoader that returns the data as an array of bytes.

Further, you can also create a derived TransformingTileLoader that automatically transforms (deserializes) the tile contents into an object.

Scala
import com.here.platform.location.dataloader.core.{TileLoader, TransformingTileLoader}

val layerId: String =
  "length" // Layer ID of any layer contained in the Optimized Map for Location Library.
def decoder(b: Array[Byte]): TileType = ??? // Provide a decoder of the byte array.
val rawLoader: TileLoader[Array[Byte]] = catalog.create(layerId)
val loader: TileLoader[TileType] = new TransformingTileLoader(rawLoader, decoder)

Each tile is uniquely identified by its TileId. These IDs are related the area that the data inside the tile describes, they follow the heretile partitioning scheme.

Retrieve Tiles Close to a Given Point

To easily discover which tiles contain the data for a particular area, Location Library provides TileResolvers.

You can get a TileResolver that is compatible with a TileLoader directly from that TileLoader.

Using a TileResolver you can, for example, look up the tiles that cover a circle of 1000m radius around the Brandenburger Tor as follows:

Scala
import com.here.platform.location.core.geospatial.GeoCoordinate
import com.here.platform.location.inmemory.geospatial.TileId

val brandenburgerTor = GeoCoordinate(52.516268, 13.377700)
val radiusInMeters = 1000.0

val outputTiles: Iterable[TileId] =
  loader.resolver.fromCenterAndRadius(brandenburgerTor, radiusInMeters)

val tiles: Iterable[(TileId, Option[TileType])] = loader.getAll(outputTiles)

The only tile within 1000 meters of the Brandenburger Tor has the tile ID 1476150. You can search for this tile within the Road Topology & Geometry layer.

Load Attributes from the Optimized Map For Location Library and HERE Map Content

This section describes how to load attributes from the Optimized Map for Location Library and from HERE Map Content.

First, to reduce the traffic on the network, you need a CacheManager. The object CacheManager contains factory methods such as withLruCache. CacheManager.withLruCache creates a CacheManager with an LRU cache policy:

Scala
import com.here.platform.location.dataloader.core.caching.CacheManager

val cacheManager = CacheManager.withLruCache()

You can find the Layers available for the Optimized Map for Location Library such as the Mapping layer in OptimizedMapLayers.

HereMapContentLayers provides the attribute Layers that are available in HERE Map Content. You can create a TileLoader for a layer, such as the navigation attributes layer, as follows:

Scala
import com.here.platform.location.integration.heremapcontent.HereMapContentLayers
import com.here.schema.rib.v2.navigation_attributes_partition.NavigationAttributesPartition

val hereMapContent: Catalog =
  catalogFactory.create(hereMapContentHRN, hereMapContentVersion)

val navigationAttributeLoader: TileLoader[NavigationAttributesPartition] =
  HereMapContentLayers.NavigationAttributes.tileLoader(hereMapContent, cacheManager)

For more information on the definition of the HRN and version of HERE Map Content, see the section Catalogs HRNs and versions.

Use the TileLoader to make the attributes format compatible with the Location Library. For more information, see the section On-the-Fly Compilation from HERE Map Content.

If you use attributes from both the Optimized Map for Location Library and from HERE Map Content, make sure that the version of the HERE Map Content is the one that was used to compile the Optimized Map for Location Library. The following snippet shows how to retrieve the correct HERE Map Content catalog:

Scala
val hereMapContent: Catalog = optimizedMap.resolveDependency(hereMapContentHRN)

Properties

Properties are values attached to vertices in the routing graph including the speed limit, accessibility by a particular type of vehicle or even the geometry (road shape). In HERE Map Content, these are called segment attributes.

Vertex Properties

Properties that always have just one value that applies for the whole vertex are represented as PropertyMaps. Vertex properties that are part of Optimized Map for Location Library are discussed in detail below.

Retrieve the Vertex for a Given Topology Segment

As mentioned in the section The Routing Graph, using vertices is more efficient to analyze properties of a given road segment than topology segments. The mapping layer of the Optimized Map for Location Library defines a mapping between vertices in the routing graph and road segments in HERE Map Content. This section demonstrates how to convert a topology segment into a vertex using this layer:

  1. Open https://platform.here.com/data/hrn:here:data::olp-here:rib-2/topology-geometry/inspect.
  2. Click a topology segment.
  3. Expand the start_node_ref object and write down its identifier.
  4. To infer the direction of the segment, click the nodes at the ends of the segment and compare their identifiers with the identifier of the segment start_node_ref.
  5. To convert the topology segment, create a TileLoader for the mapping layer and a TiledReverseHereMapContentReferencePropertyMap. The TiledReverseHereMapContentReferencePropertyMap consumes the partition that the TileLoader produces for a given tile ID. The following code snippet demonstrates how to retrieve the vertex that corresponds to the topology segment here:cm:segment:94480838 in partition 23618402:

    Scala
    import com.here.platform.location.integration.heremapcontent.PartitionId
    import com.here.platform.location.integration.optimizedmap.OptimizedMapLayers
    import com.here.platform.location.integration.optimizedmap.geospatial.{
      HereMapContentReference,
      SegmentId
    }
    import com.here.platform.location.integration.optimizedmap.graph.{
      MappingTile,
      TiledReverseHereMapContentReferencePropertyMap
    }
    
    val mappingLoader = OptimizedMapLayers.Mapping.tileLoader(optimizedMap, cacheManager)
    
    val mappingPartitions: Map[TileId, Option[MappingTile]] = mappingLoader
      .getAll(outputTiles)
      .toMap
    
    val optimizedMapMapping = new TiledReverseHereMapContentReferencePropertyMap(
      mappingPartitions,
      mappingLoader.resolver
    )
    
    val srcTopologySegment = HereMapContentReference(PartitionId("23618402"),
                                                     SegmentId("here:cm:segment:94480838"),
                                                     Forward)
    val vertex = optimizedMapMapping(srcTopologySegment)
    
    println(s"The topology segment $srcTopologySegment corresponds to the vertex $vertex")

Retrieve the Topology Segment for a Given Vertex

Several Location Library algorithms, such as proximity search, return vertices instead of topology segments. Vertices have two drawbacks:

Therefore, you may need to convert vertices to a topology segment. The following code snippet demonstrates how to perform this operation by creating a TileLoader for the mapping layer and a TiledHereMapContentReferencePropertyMap. The TiledHereMapContentReferencePropertyMap consumes the partition that the TileLoader produces for a given tile ID. The code snippet uses the vertex retrieved in the code snippet of the previous section.

Scala
import com.here.platform.location.integration.optimizedmap.OptimizedMapLayers
import com.here.platform.location.integration.optimizedmap.geospatial.HereMapContentReference
import com.here.platform.location.integration.optimizedmap.graph.{
  MappingTile,
  TiledHereMapContentReferencePropertyMap
}

val mappingLoader = OptimizedMapLayers.Mapping.tileLoader(optimizedMap, cacheManager)

val mappingPartitions: Map[TileId, Option[MappingTile]] = mappingLoader
  .getAll(outputTiles)
  .toMap

val hereMapContentMapping = new TiledHereMapContentReferencePropertyMap(mappingPartitions)

val topologySegment = hereMapContentMapping(vertex)
val HereMapContentReference(partitionId, segmentId, direction) = topologySegment

println(s"$vertex corresponds to the segment with identifier $segmentId")
println(s"  in HERE Map Content partition $partitionId")
println(s"  travelling in $direction direction, relative to the segment")

Retrieve the Geometry of a Given Vertex

As mentioned in The Routing Graph, a vertex represents travel in a particular direction along a road segment. The geometry layer in the Optimized Map for Location Library stores the shapes of the road segments. A tile of the layer contains the following:

  • The road shape of each segment that crosses the tile
  • A spatial index for efficient searches within a given area

Creating a property map from geometry layer partitions is enabled by TiledGeometryPropertyMap. The TiledGeometryPropertyMap returns a directed geometry for each vertex: the points of the geometry occur in the travel direction of this vertex.

Scala
import com.here.platform.location.core.graph.PropertyMap
import com.here.platform.location.inmemory.geospatial.{GeometryTile, PackedLineString}

val geometryLoader =
  OptimizedMapLayers.Geometry.tileLoader(optimizedMap, cacheManager)

val geometryTiles: Iterable[(TileId, Option[GeometryTile])] =
  geometryLoader.getAll(outputTiles)
val geometry: PropertyMap[Vertex, PackedLineString] =
  new TiledGeometryPropertyMap(geometryTiles.toMap)

val tileId = outputTiles.head
val segmentIndex = SegmentIndex.fromVertexIndex(VertexIndex(10))

val forwardVertex = Vertex(tileId, segmentIndex.toVertexIndex(Forward))
val backwardVertex = Vertex(tileId, segmentIndex.toVertexIndex(Backward))

assert(geometry(forwardVertex).toIndexedSeq.reverse == geometry(backwardVertex).toIndexedSeq)

The Length Layer

The length layer provides the lengths of vertices in meters. To access the length of a vertex, you first have to create an TiledUndirectedPropertyMap that contains the length data. Then use this TiledUndirectedPropertyMap to access the length of a given vertex.

Scala
val lengthLoader = OptimizedMapLayers.Length.tileLoader(optimizedMap, cacheManager)

val lengthPartitions: Iterable[(TileId, Option[UndirectedPropertyTile[Double]])] =
  lengthLoader.getAll(outputTiles)
val lengthProperty: PropertyMap[Vertex, Double] =
  new TiledUndirectedPropertyMap(lengthPartitions.toMap)

val lengthVertex = Vertex(outputTiles.head, VertexIndex(10))

val length = lengthProperty(lengthVertex)
println(s"The $lengthVertex is $length meters long.")

Range-Based Vertex Properties

In HERE Map Content, value ranges represent properties that can potentially cover many road segments or just parts of them. The Optimized Map for Location Library contains a simplified representation of these value ranges comprising a property value as well as a start and end fraction along a single vertex's length. This still allows you to attach a particular property value to only parts of a vertex but eliminates the complexity caused by multi-vertex or even multi-partition ranges.

A RangeBasedPropertyMap can represent Range Based Properties. You can use this method to retrieve all property ranges defined on a vertex or the particular range in effect at some fraction of the vertex.

Scala
import com.here.platform.location.core.graph.RangeBasedProperty

val v = Vertex(outputTiles.head, VertexIndex(17))
val vertexFraction = 0.5
val rangesOnVertex: Seq[RangeBasedProperty[SpeedLimitKmh]] = propertyMap(v)
val speedLimit: RangeBasedProperty[SpeedLimitKmh] =
  propertyMap(v, vertexFraction).get

println(s"The speed limit on $v at fraction $vertexFraction is ${speedLimit.value} km/h.")
println(s"There are ${rangesOnVertex.length} value ranges defined on this vertex.")

From the Optimized Map for Location Library

The Optimized Map for Location Library contains some range based property layers that are compiled from HERE Map Content.

One example is the roadaccess layer, which contains information on what type of traffic can access a particular vertex.

Scala
val roadAccessType: RangeBasedPropertyMap[Vertex, RoadAccessType] =
  PropertyMaps.roadAccess(optimizedMap, cacheManager)

val vertexHasCarAccessType: Boolean =
  roadAccessType(v, vertexFraction).get.value.intersects(RoadAccess.Automobile)

val isCarOrBusAccessible =
  PropertyMaps.roadAccess(optimizedMap,
                          cacheManager,
                          RoadAccess.Automobile union RoadAccess.Bus)

val vertexIsCarOrBusAccessible: Boolean = isCarOrBusAccessible(v, vertexFraction).get.value

println(s"If $v has the car access type ($vertexHasCarAccessType) it is accessible")
println(s"for cars or buses ($vertexIsCarOrBusAccessible)")

Another example is the freeflowspeed layer, witch contains the speed at which a car is expected to traverse the various portions of a vertex geometry, under normal traffic conditions, expressed in km/h.

Scala
val freeFlowSpeed = PropertyMaps.freeFlowSpeed(optimizedMap, cacheManager)
println(s"The free-flow speeds of $v are ${freeFlowSpeed(v)}")

On-the-Fly Compilation from HERE Map Content

There are properties available as HERE Map Content Segment Attributes (for example, in the road layer) but not part of the precompiled Optimized Map for Location Library. You can compile these properties to RangeBasedPropertyMaps on the fly.

This requires the following data:

  • The Attribute layer from HERE Map Content containing the property to be compiled:

    Scala
    val attributeLoader =
      HereMapContentLayers.NavigationAttributes.tileLoader(hereMapContent, cacheManager)
  • The PartitionResolver for that attribute layer:

    Scala
    val partitionResolver =
      HereMapContentLayers.NavigationAttributes.partitionResolver(optimizedMap, cacheManager)

    Note

    The PartitionResolver is required because of the way HERE Map Content stores attributes. It uses information from the Optimized Map for Location Library to determine which partitions from the attribute layer are necessary to compile properties for a particular graph partition.

  • The Mapping layer from the Optimized Map for Location Library, described above

  • The partitions of the graph whose properties you want to query (the output tiles)

Now you can construct a RangeBasedPropertyMapFactory that allows you to compile any attribute stored in the chosen attribute layer to a property map for the requested output partitions.

Scala
val propertyMapFactory =
  new RangeBasedPropertyMapFactory(
    attributeLoader,
    mappingLoader,
    partitionResolver
  )(outputTiles)

To identify which attribute from the attribute layer you are interested in, you need to create an AttributeAccessor.

Scala
import com.here.platform.location.compilation.heremapcontent.{
  AttributeAccessor,
  AttributeAccessors
}
import com.here.schema.rib.v2.common_attributes.SpeedLimitAttribute
import com.here.schema.rib.v2.navigation_attributes_partition.NavigationAttributesPartition

type SpeedLimitKmh = Int
val attributeAccessor: AttributeAccessor[NavigationAttributesPartition, SpeedLimitKmh] =
  AttributeAccessors
    .forHereMapContentSegmentAnchor[NavigationAttributesPartition,
                                    SpeedLimitAttribute,
                                    SpeedLimitKmh](
      _.speedLimit,
      _.value
    )

With that, you can then compile the final property map.

Scala
import com.here.platform.location.core.graph.RangeBasedPropertyMap

val propertyMap: RangeBasedPropertyMap[Vertex, SpeedLimitKmh] =
  propertyMapFactory.createPropertyMapFor(attributeAccessor)

results matching ""

    No results matching ""