GeoJSON Serialization

To easily serialize the library's geometry objects into GeoJSON format, you can use the location-io package and store the resulting GeoJSON in a file. Use your preferred viewer or store the GeoJSON in a versioned layer and view it through https://platform.hereolp.cn/data.

The main entry point of the package is the FeatureCollection class, which you can use to add and serialize features.

You can add:

  • Geo coordinates as point markers
  • Line strings as geometries or arrows to highlight a line string direction
  • Range based attributes on line strings, with one geometry (or an arrow) for each range
  • Point based attributes on line strings, with one marker at each attribute position
  • Bounding box to highlight a rectangle area on a map
  • Custom marker images

Creating a marker

To add a marker at a certain location, you can add any geo-coordinate object as a point.

Scala
Java
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

FeatureCollection()
  .point(coordinates, SimpleStyleProperties().markerColor(Color.Green))
  .writePretty(new FileOutputStream("point-marker.json"))
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
new FeatureCollection()
    .point(coordinates, new SimpleStyleProperties().markerColor(Color.GREEN))
    .writePretty(new FileOutputStream("point-marker.json"));
A point marker on the map
Figure 1. A point marker on the map

You can use SimpleStyleProperties or HereProperties and combine them also with your custom properties using the add method.

For Java, please use the corresponding similar classes from the javadsl package.

Note

In Scala, geospatial types need to implement the corresponding operations, e.g GeoCoordinateOperations for geo-coordinates or
LineStringOperations for line strings.

In Java, geospatial types need to implement the corresponding holder, e.g. GeoCoordinateHolder

Drawing a road geometry for a vertex

The following code will result in a red segment geometry highlighting a vertex.

Scala
Java
import com.here.platform.location.integration.optimizedmap.graph.PropertyMaps
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

val geometry = PropertyMaps(optimizedMap).geometry
FeatureCollection()
  .lineString(geometry(vertex), SimpleStyleProperties().strokeWidth(5.0).stroke(Color.Red))
  .writePretty(new FileOutputStream("vertex-geometry.json"))
import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.core.geospatial.javadsl.LineStringHolder;
import com.here.platform.location.core.graph.javadsl.PropertyMap;
import com.here.platform.location.inmemory.graph.Vertex;
import com.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
PropertyMap<Vertex, LineStringHolder<GeoCoordinate>> geometry =
    new PropertyMaps(optimizedMap).geometry();
new FeatureCollection()
    .lineString(
        geometry.get(vertex), new SimpleStyleProperties().strokeWidth(5.0).stroke(Color.RED))
    .writePretty(new FileOutputStream("vertex-geometry.json"));

Drawing a range on a road geometry for a vertex

Most attributes are referenced using ranges on vertex geometries, and you can use specific methods to highlight them.

Scala
Java
import com.here.platform.location.integration.optimizedmap.graph.PropertyMaps
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

val geometry = PropertyMaps(optimizedMap).geometry
FeatureCollection()
  .lineStringRanges(geometry(vertex),
                    Seq(RangeBasedProperty(0, 0.5, Color.Red),
                        RangeBasedProperty(0.5, 1.0, Color.Green)))(colorRange =>
    SimpleStyleProperties().strokeWidth(5.0).stroke(colorRange.value))
  .writePretty(new FileOutputStream("vertex-range.json"))
import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.core.geospatial.javadsl.LineStringHolder;
import com.here.platform.location.core.graph.RangeBasedProperty;
import com.here.platform.location.core.graph.javadsl.PropertyMap;
import com.here.platform.location.inmemory.graph.Vertex;
import com.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
PropertyMap<Vertex, LineStringHolder<GeoCoordinate>> geometry =
    new PropertyMaps(optimizedMap).geometry();
new FeatureCollection()
    .lineStringRanges(
        geometry.get(vertex),
        Arrays.asList(
            new RangeBasedProperty<>(0.0, 0.5, Color.RED),
            new RangeBasedProperty<>(0.5, 1.0, Color.GREEN)),
        colorRange -> new SimpleStyleProperties().strokeWidth(5.0).stroke(colorRange.value()))
    .writePretty(new FileOutputStream("vertex-range.json"));

This results in the geometry of the vertex being red for the first half, and green for the second.

Two ranges on a vertex geometry
Figure 2. Two ranges on a vertex geometry

Use arrows to display directional attributes on ranges

With the following code you can now show ranged attributes from a property map in a GeoJSON file.

Scala
Java
import com.here.platform.location.integration.optimizedmap.geospatial.ProximitySearches
import com.here.platform.location.integration.optimizedmap.graph.PropertyMaps
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

val geometry = PropertyMaps(optimizedMap).geometry
val automobileAccess =
  PropertyMaps(optimizedMap).roadAccess(RoadAccess.Automobile)
val proximitySearch = ProximitySearches(optimizedMap).vertices

proximitySearch
  .search(coordinates, 200)
  .foldLeft(FeatureCollection())((featureCollection, result) =>
    featureCollection.arrowRanges(geometry(result.element), automobileAccess(result.element))(
      access =>
        SimpleStyleProperties()
          .stroke(if (access.value) Color.Green else Color.Red)))
  .writePretty(new FileOutputStream("road-access.json"))
import com.here.platform.location.core.geospatial.ElementProjection;
import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.core.geospatial.javadsl.GeoCoordinateHolder;
import com.here.platform.location.core.geospatial.javadsl.LineStringHolder;
import com.here.platform.location.core.geospatial.javadsl.ProximitySearch;
import com.here.platform.location.core.graph.javadsl.PropertyMap;
import com.here.platform.location.core.graph.javadsl.RangeBasedPropertyMap;
import com.here.platform.location.inmemory.graph.Vertex;
import com.here.platform.location.integration.optimizedmap.geospatial.javadsl.ProximitySearches;
import com.here.platform.location.integration.optimizedmap.graph.RoadAccess;
import com.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
PropertyMap<Vertex, LineStringHolder<GeoCoordinate>> geometry =
    new PropertyMaps(optimizedMap).geometry();
RangeBasedPropertyMap<Vertex, Boolean> automobileAccess =
    new PropertyMaps(optimizedMap).roadAccess(RoadAccess.Automobile);
ProximitySearch<GeoCoordinateHolder, Vertex> proximitySearch =
    new ProximitySearches(optimizedMap).vertices();

Iterable<ElementProjection<Vertex>> searchResults = proximitySearch.search(coordinates, 200);

FeatureCollection featureCollection = new FeatureCollection();
for (ElementProjection<Vertex> result : searchResults) {
  featureCollection.arrowRanges(
      geometry.get(result.getElement()),
      automobileAccess.get(result.getElement()),
      access -> new SimpleStyleProperties().stroke(access.value() ? Color.GREEN : Color.RED));
}

featureCollection.writePretty(new FileOutputStream("road-access.json"));

Green arrows highlight roads traversable by cars.

Road acces with arrows
Figure 3. Road acces with arrows

Drawing a point based attributes on a road geometry for a vertex

Similarly to ranges, attributes can be referred by point-based properties on vertex geometries.

Scala
Java
import com.here.platform.location.integration.optimizedmap.graph.PropertyMaps
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

val geometry = PropertyMaps(optimizedMap).geometry
FeatureCollection()
  .lineStringPoints(geometry(vertex),
                    Seq(PointBasedProperty(0, Color.Red),
                        PointBasedProperty(0.5, Color.Green),
                        PointBasedProperty(1.0, Color.Blue)))(colorPoint =>
    SimpleStyleProperties().markerColor(colorPoint.value))
  .writePretty(new FileOutputStream("vertex-point.json"))
import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.core.geospatial.javadsl.LineStringHolder;
import com.here.platform.location.core.graph.PointBasedProperty;
import com.here.platform.location.core.graph.javadsl.PropertyMap;
import com.here.platform.location.inmemory.graph.Vertex;
import com.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
PropertyMap<Vertex, LineStringHolder<GeoCoordinate>> geometry =
    new PropertyMaps(optimizedMap).geometry();
new FeatureCollection()
    .lineStringPoints(
        geometry.get(vertex),
        Arrays.asList(
            new PointBasedProperty<>(0.0, Color.RED),
            new PointBasedProperty<>(0.5, Color.GREEN),
            new PointBasedProperty<>(1.0, Color.BLUE)),
        colorPoint -> new SimpleStyleProperties().markerColor(colorPoint.value()))
    .writePretty(new FileOutputStream("vertex-point.json"));

This results in the three point markers over a vertex geometry.

Three points on a vertex geometry
Figure 4. Three points on a vertex geometry

Drawing a bounding box to highlight a rectangle area

The following code will highlight a map area.

Scala
Java
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

FeatureCollection()
  .boundingBox(boundingBox, SimpleStyleProperties().fill(Color.Green).fillOpacity(0.5))
  .writePretty(new FileOutputStream("bounding-box.json"))
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
new FeatureCollection()
    .boundingBox(boundingBox, new SimpleStyleProperties().fill(Color.GREEN).fillOpacity(0.5))
    .writePretty(new FileOutputStream("point-marker.json"));

This results in a transparent green rectangle on the map.

A bounding box on the map
Figure 5. A bounding box on the map

Custom marker symbol images

The markerImage method of HereProperties accepts a valid data URL or a web URL, making it possible to display an external image as marker image.

Images can also be referenced by name through the markerSymbol method of SimpleStyleProperties; such names can be defined through the markerSymbolImages method of FeatureCollection.

Scala
Java
import com.here.platform.location.io.scaladsl.geojson.{
  FeatureCollection,
  MarkerSymbolImages,
  SimpleStyleProperties
}

FeatureCollection()
  .markerSymbolImages(
    MarkerSymbolImages()
      .add(
        "img",
        "data:image/svg+xml;utf8,<svg width='128' height='128' viewBox='0 0 26 26' version='1.1' xmlns='http://www.w3.org/2000/svg'><g style='fill:rgb(217, 217, 242); stroke:rgba(198, 203, 221, 0.5)' transform='translate(0,5)'><path d='M 5,7 C 4.5,4.5 7.5,1.5 10,3 c 5.5,-5 13,-1.5 12.5,5 4,2 3.5,7.5 -0,7.5 -6,0 -10.5,0 -18.5,0 -5,-0 -4.5,-8.5 1,-8.5 z'/></g></svg>"
      ))
  .point(coordinates, SimpleStyleProperties().markerSymbol("img"))
  .writePretty(new FileOutputStream("custom-marker.json"))
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.MarkerSymbolImages;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;

new FeatureCollection()
    .markerSymbolImages(
        new MarkerSymbolImages()
            .add(
                "img",
                "data:image/svg+xml;utf8,<svg width='128' height='128' viewBox='0 0 26 26' version='1.1' xmlns='http://www.w3.org/2000/svg'><g style='fill:rgb(217, 217, 242); stroke:rgba(198, 203, 221, 0.5)' transform='translate(0,5)'><path d='M 5,7 C 4.5,4.5 7.5,1.5 10,3 c 5.5,-5 13,-1.5 12.5,5 4,2 3.5,7.5 -0,7.5 -6,0 -10.5,0 -18.5,0 -5,-0 -4.5,-8.5 1,-8.5 z'/></g></svg>"))
    .point(coordinates, new SimpleStyleProperties().markerSymbol("img"))
    .writePretty(new FileOutputStream("custom-marker.json"));

This results in a cloud logo being shown as marker symbol image.

Cloud logo as custom marker
Figure 6. Cloud logo as custom marker

Drawing on top of a background

If you add features to an already populated feature collection, the latter will be used as a background for the former.

Scala
Java
import com.here.platform.location.inmemory.graph.{Forward, Vertices}
import com.here.platform.location.integration.optimizedmap.geospatial.ProximitySearches
import com.here.platform.location.integration.optimizedmap.graph.PropertyMaps
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}

val geometry = PropertyMaps(optimizedMap).geometry
val proximitySearch = ProximitySearches(optimizedMap).vertices

val searchResults = proximitySearch
  .search(coordinates, 200)

val background = searchResults
  .map(result =>
    Feature.lineString(geometry(result.element),
                       SimpleStyleProperties().strokeWidth(8.0).stroke(Color.Gray)))

val arrowsOnBackground = searchResults.map(result =>
  Feature.arrow(
    geometry(result.element),
    SimpleStyleProperties()
      .stroke(if (Vertices.directionOf(result.element) == Forward) Color.Blue else Color.Red)
  ))

FeatureCollection(background ++ arrowsOnBackground).writePretty(
  new FileOutputStream("arrows-on-geometries.json"))
import com.here.platform.location.core.geospatial.ElementProjection;
import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.core.geospatial.javadsl.LineStringHolder;
import com.here.platform.location.core.geospatial.javadsl.ProximitySearch;
import com.here.platform.location.core.graph.javadsl.PropertyMap;
import com.here.platform.location.inmemory.graph.Vertex;
import com.here.platform.location.inmemory.graph.Vertices;
import com.here.platform.location.inmemory.graph.javadsl.Direction;
import com.here.platform.location.integration.optimizedmap.geospatial.javadsl.ProximitySearches;
import com.here.platform.location.integration.optimizedmap.graph.javadsl.PropertyMaps;
import com.here.platform.location.io.javadsl.Color;
import com.here.platform.location.io.javadsl.geojson.FeatureCollection;
import com.here.platform.location.io.javadsl.geojson.SimpleStyleProperties;
PropertyMap<Vertex, LineStringHolder<GeoCoordinate>> geometry =
    new PropertyMaps(optimizedMap).geometry();

ProximitySearch<GeoCoordinate, Vertex> proximitySearch =
    new ProximitySearches(optimizedMap).vertices();
Iterable<ElementProjection<Vertex>> searchResults = proximitySearch.search(coordinates, 200);

FeatureCollection featureCollection = new FeatureCollection();

for (ElementProjection<Vertex> result : searchResults)
  featureCollection.lineString(
      geometry.get(result.element()),
      new SimpleStyleProperties().strokeWidth(8.0).stroke(Color.GRAY));

for (ElementProjection<Vertex> result : searchResults)
  featureCollection.arrow(
      geometry.get(result.element()),
      new SimpleStyleProperties()
          .stroke(
              Vertices.directionOf(result.element()) == Direction.FORWARD
                  ? Color.BLUE
                  : Color.GREEN));

featureCollection.writePretty(new FileOutputStream("arrows-on-geometries.json"));

This results in arrows being drawn over road geometry, with forward vertices drawn in blue and backward vertices drawn in red.

Arrows with road geometries in background
Figure 7. Arrows with road geometries in background

Write a GeoJSON to a catalog in Spark

If you write GeoJSON to a versioned partition, you will be able to see the results on https://platform.hereolp.cn/data.

In order to demonstrate this functionality we will use the Data Client Spark Connector, but you can choose to write the resulting GeoJSON using other Data Client methods or the Data Processing Library.

Scala
import com.here.platform.data.client.spark.LayerDataFrameWriter._
import com.here.platform.location.integration.herecommons.geospatial.{
  HereTileLevel,
  HereTileResolver
}
import com.here.platform.location.integration.optimizedmap.geospatial.ProximitySearches
import com.here.platform.location.integration.optimizedmap.graph.PropertyMaps
import com.here.platform.location.io.scaladsl.Color
import com.here.platform.location.io.scaladsl.geojson.{FeatureCollection, SimpleStyleProperties}
val sparkSession: SparkSession =
  SparkSession
    .builder()
    .master("local")
    .appName("location-io-example")
    .getOrCreate()
import sparkSession.implicits._
import sparkSession.sparkContext._

case class StringConverter(field: String) extends VersionedDataConverter {
  override def serializeGroup(rowMetadata: VersionedRowMetadata, rows: Iterator[Row]) =
    GroupedData(rowMetadata, rows.next().getAs[String](field).getBytes("UTF-8"))
}

parallelize(Seq((berlinGeoCoordinates1, Color.Red), (berlinGeoCoordinates2, Color.Blue)))
  .flatMap {
    case (coordinate, color) =>
      val proximity = ProximitySearches(optimizedMapLayers).vertices
      val geometry = PropertyMaps(optimizedMapLayers).geometry
      val resolver = new HereTileResolver(HereTileLevel(12))
      proximity
        .search(coordinate, 500)
        .map(p => geometry(p.element))
        .map(g => (resolver.fromCoordinate(g(0)), g, color))
  }
  .groupBy(_._1)
  .map {
    case (tile, geometriesWithColor) =>
      tile.value.toString -> FeatureCollection(geometriesWithColor.map {
        case (_, g, c) => Feature.lineString(g, SimpleStyleProperties().stroke(c))
      }).toJson

  }
  .toDF("mt_partition", "geojson")
  .writeLayer(outputCatalogHrn, outputLayer)
  .withDataConverter(StringConverter("geojson"))
  .save()
sparkSession.stop()

In the following example, you start from an RDD of coordinates and colors, perform a distributed proximity search for geometries around the points in the RDD, and finally create a one GeoJSON string for each output partition, using the colors from the input RDD.

The results can be observed in the target catalog, when the layer has HEREtile partitioning and application/vnd.geo+json as mime-type.

GeoJSON on the portal
Figure 8. GeoJSON on the portal

results matching ""

    No results matching ""