Calculate partition Tile IDs

Objectives: Calculate partition Tile IDs for certain geocoordinate area queries and levels

Complexity: Beginner

Time to complete: 30 mins

Source code: Download

The purpose of this tutorial is to learn how to look up the Tiles that cover a certain area in a location using the HereTileResolver Java and HereTileResolver Scala classes from the Location Library.

The tutorial covers the following topics:

HERE Tile Partitioning

Tiling is a process of dividing map data into partitions. The HERE Tiling scheme is based on a quadtree. A quadtree is a tree data structure in which each internal node has four children.

Quadtree partitions a two-dimensional space by recursively subdividing it into four Tiles. The child Tiles are numbered 0-3 in a fixed reverse S pattern.

Each Tile in the map has an identifier called a HERE Tile ID. A HERE Tile ID is a 64-bit unsigned integer computed from the tile's quadkey. A quadkey is a string of numbers (0-3) that captures the hierarchy of parent-child Tiles from level one to the target Tile level.

For example, for the Tile of level 5 containing San Francisco in the map, the quadkey would be 02123 because the parent Tile is 0212, and the child Tile containing San Francisco is 3. The Tile ID happens to be 1179.

Map
Figure 1. Map

However, you do not need to calculate the Tile IDs yourself. You can lean on the HereTileResolver class to calculate Tile IDs for various area queries. This class is part of the Location Library. In the following chapters, you will create applications to get Tile information using the Tile ID and calculate Tile IDs according to the HERE Tile partitioning scheme.

For more details on HERE tile partitioning, see Partitions.

Set up the Maven project

Download the source code in the beginning of the tutorial and put it in a folder of your choice, or create a folder structure from scratch for your project:

here-tile-resolver
└── src
    └── main
        ├── java
        └── resources
        └── scala

You can do this with a single bash command:

mkdir -p tile-resolver/src/main/{java,resources,scala}

The Maven POM file is similar to the one in the Verify Maven Settings example, however with a simplified parent POM and dependencies sections:

Parent POM:

<parent>
    <groupId>com.here.platform</groupId>
    <artifactId>sdk-standalone-bom_2.12</artifactId>
    <version>2.54.3</version>
    <relativePath/>
</parent>

Dependencies:

<dependencies>

    <dependency>
        <groupId>com.here.platform.location</groupId>
        <artifactId>location-integration-here-commons_2.12</artifactId>
    </dependency>

</dependencies>

Get information about a Tile

Location Library provides basic methods for retrieving information about a given Tile ID.

The following tutorial demonstrates how to use the HereTileLevel and HereTileResolver classes to get:

  • Zoom Level for the given Tile ID
  • BoundingBox for the given Tile ID

using San Francisco Tile ID 19319030.

The following list explains the terminology that occur in this tutorial:

  • Zoom Level determines how much of the world is visible on a map. Location Library supports a maximum of 31 zoom levels, with 0 being the lowest zoom level (fully zoomed out) and 31 being the highest (fully zoomed in). At lower zoom levels, a smaller set of map Tiles covers a large geographical area. At higher zoom levels, a larger number of Tiles cover a smaller geographical area.

The following picture shows Tiles on 1, 11, and 14 zoom levels: Tiles on `1`, `11`, and `14` zoom levels

  • BoundingBox (usually shortened to bbox) is an area defined by two longitudes and two latitudes, where:
    • Latitude is a decimal number between -90.0 and 90.0.
    • Longitude is a decimal number between -180.0 and 180.0.

The picture below shows bbox with coordinates { north : 37.79287, south : 37.68127, east : -122.34480, west : -122.54244 } on the map:

Example of Bounding Boxing with coordinates { north : 37.79287, south : 37.68127, east : -122.34480, west : -122.54244 }
Figure 2. Example of Bounding Boxing with coordinates { north : 37.79287, south : 37.68127, east : -122.34480, west : -122.54244 }

The Location Library provides the following methods to get information for given Tile ID:

  • apply() from the HereTileLevel class
  • boundingBoxOf(HereTile tileId) from the HereTileResolver class

With the following code snippet , you can get all information about San Francisco Tile ID 19319030 described above:

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.BoundingBox
import com.here.platform.location.inmemory.geospatial.TileId
import com.here.platform.location.integration.herecommons.geospatial.{
  HereTileLevel,
  HereTileResolver
}

object HereTileInformationTutorialScala {

  def main(args: Array[String]): Unit = {

    val sanFranciscoTileId = TileId(19319030)

    val zoomLevel = HereTileLevel.apply(sanFranciscoTileId)
    println(s"Zoom Level for Tile ${sanFranciscoTileId.value} is ${zoomLevel.value}")

    val boundingBox: BoundingBox = HereTileResolver.boundingBoxOf(sanFranciscoTileId)
    printf(s"Bounding box for Tile ${sanFranciscoTileId.value} is $boundingBox")

  }
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.BoundingBox;
import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel;
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver;

public class HereTileInformationTutorial {

  public static void main(String[] args) {

    long sanFranciscoTileId = 19319030L;

    int zoomLevel = HereTileLevel.apply(sanFranciscoTileId).value();

    System.out.printf("Zoom Level for Tile %s is %s%n", sanFranciscoTileId, zoomLevel);

    BoundingBox boundingBox = HereTileResolver.boundingBoxOf(sanFranciscoTileId);
    System.out.printf("Bounding box for Tile %s is %s", sanFranciscoTileId, boundingBox);
  }
}

To execute the application, run the following command:

Scala
Java

mvn package exec:java -D"exec.mainClass"="HereTileInformationTutorialScala"


mvn package exec:java -D"exec.mainClass"="HereTileInformationTutorial"

The result of the following application is:

Zoom Level for Tile 19319030 is 12
Bounding box for Tile 19319030 is BoundingBox(37.79296875,37.705078125,-122.431640625,-122.51953125)

Quad-Tree traversal

Every Tile has one ancestor Tile and one or more descendants.

  • Ancestor Tile is a Tile at a lower zoom level which has descendant Tiles on the same area of the map with a higher zoom level.
  • Descendant Tiles are Tiles at a higher zoom level, which are included in the area on the map of the ancestor Tile. The number of descendant Tiles depends on the zoom level. For example, Tile on zoom level n has 4 descendant Tiles on zoom level n+1 and 16 descendant tiles on zoom level n+2.

The following figure shows an ancestor tile on level 12 with the blue border and 16 descendant Tiles on level 14 with the red borders.

Ancestor and descendant Tiles
Figure 3. Ancestor and descendant Tiles

The HereTileResolver class provides the following methods to get ancestor and descendant Tiles:

  • fromDescendantTile(descendantTileId) [ Java API | Scala API ] to get an ancestor Tile for the given descendant Tile ID. If the tile is at the highest level, and it has no parents, then the current tile's ID is returned.
  • fromAncestorTile(ancestorTileId) [ Java API | Scala API ] to get descendant Tiles for given ancestor Tile ID.

Using the following code snippet, you can get descendant tiles on 14 zoom level using San Francisco Tile ID 19319030 on 12 zoom level, and for one of the resulting descendant Tile get ancestor Tile on zoom level 12. The resulting ancestor Tile is a San Francisco Tile ID 19319030 that we passed on to obtain descent Tiles.

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.inmemory.geospatial.TileId
import com.here.platform.location.integration.herecommons.geospatial.{
  HereTileLevel,
  HereTileResolver
}

object QuadTreeTraversalTutorialScala {

  val ZOOM_LEVEL_TWELVE = 12
  val ZOOM_LEVEL_FOURTEEN = 14

  def main(args: Array[String]): Unit = {

    val sanFranciscoTileId = 19319030

    val resolverTwelve: HereTileResolver = new HereTileResolver(HereTileLevel(ZOOM_LEVEL_TWELVE))
    val resolverFourteen: HereTileResolver = new HereTileResolver(
      HereTileLevel(ZOOM_LEVEL_FOURTEEN))

    val descendantTiles: Set[TileId] =
      resolverFourteen.fromAncestorTile(TileId(sanFranciscoTileId))

    println(
      s"The Tile $sanFranciscoTileId (level $ZOOM_LEVEL_TWELVE) has the following descendant Tiles on level $ZOOM_LEVEL_FOURTEEN: ${descendantTiles.map(_.value).mkString(", ")}")

    val sanFranciscoDescendantTile = descendantTiles.head

    val ancestorTile: TileId =
      resolverTwelve.fromDescendantTile(sanFranciscoDescendantTile)
    println(
      s"The tile ${sanFranciscoDescendantTile.value} (level $ZOOM_LEVEL_FOURTEEN) has the following ancestor Tile on level $ZOOM_LEVEL_TWELVE: ${ancestorTile.value}")

  }
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel;
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver;
import java.util.Set;

public class QuadTreeTraversalTutorial {

  public static final int ZOOM_LEVEL_TWELVE = 12;
  public static final int ZOOM_LEVEL_FOURTEEN = 14;

  public static void main(String[] args) {

    int sanFranciscoTileId = 19319030;

    HereTileResolver resolverTwelve = new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_TWELVE));
    HereTileResolver resolverFourteen =
        new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_FOURTEEN));

    Set<Long> descendantTiles = resolverFourteen.fromAncestorTile(sanFranciscoTileId);

    System.out.printf(
        "The Tile %s (level %s) has the following descendant Tiles on level %s: %s%n",
        sanFranciscoTileId, ZOOM_LEVEL_TWELVE, ZOOM_LEVEL_FOURTEEN, descendantTiles);

    long sanFranciscoDescendantTile = descendantTiles.stream().findFirst().get();

    long ancestor = resolverTwelve.fromDescendantTile(sanFranciscoDescendantTile);

    System.out.printf(
        "The Tile %s (level %s) has the following ancestor Tile on level %s: %s",
        sanFranciscoDescendantTile, ZOOM_LEVEL_FOURTEEN, ZOOM_LEVEL_TWELVE, ancestor);
  }
}

To execute the application, run the following command:

Scala
Java

mvn package exec:java -D"exec.mainClass"="QuadTreeTraversalTutorialScala"


mvn package exec:java -D"exec.mainClass"="QuadTreeTraversalTutorial"

The result of the following application is:

The Tile 19319030 (level 12) has the following descendant Tiles on level 14: [309104494, 309104484, 309104489, 309104485, 309104481, 309104493, 309104486, 309104482, 309104492, 309104491, 309104483, 309104487, 309104480, 309104490, 309104495, 309104488]
The Tile 309104494 (level 14) has the following ancestor Tile on level 12: 19319030

In case you need to get a Tile that includes a specific geocoordinate, use the fromCoordinate(geoCoordinate) method from the Location Library HereTileResolver [ Java API | Scala API ] class.

With the following code snippet, you can get a Tile ID on zoom level 12 that covers Golden Gate Bridge in San Francisco.

Geocoordinate instance is created using Golden Gate Bridge Latitude 37.82171 and Golden Gate Bridge Longitude -122.47881.

Tile that covers Golden Gate Bridge in San Francisco
Figure 4. Tile that covers Golden Gate Bridge in San Francisco

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.GeoCoordinate
import com.here.platform.location.inmemory.geospatial.TileId
import com.here.platform.location.integration.herecommons.geospatial.{
  HereTileLevel,
  HereTileResolver
}

object HereTileWithPointTutorialScala {

  val ZOOM_LEVEL_TWELVE = 12

  def main(args: Array[String]): Unit = {

    val goldenGateBrgLatitude = 37.82171
    val goldenGateBrgLongitude = -122.47881

    val hereTileResolver = new HereTileResolver(HereTileLevel(ZOOM_LEVEL_TWELVE))

    val geoCoordinateSanFrancisco = GeoCoordinate(goldenGateBrgLatitude, goldenGateBrgLongitude)

    val tileId = hereTileResolver.fromCoordinate(geoCoordinateSanFrancisco)

    println(
      s"Tile ${tileId.value} on level $ZOOM_LEVEL_TWELVE containing the point $geoCoordinateSanFrancisco")

  }
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel;
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver;

public class HereTileWithPointTutorial {

  public static final int ZOOM_LEVEL_TWELVE = 12;

  public static void main(String[] args) {

    double goldenGateBrgLatitude = 37.82171;
    double goldenGateBrgLongitude = -122.47881;

    HereTileResolver hereTileResolver = new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_TWELVE));

    GeoCoordinate geoCoordinateSanFrancisco =
        new GeoCoordinate(goldenGateBrgLatitude, goldenGateBrgLongitude);

    Long tileId = hereTileResolver.fromCoordinate(geoCoordinateSanFrancisco);

    System.out.printf(
        "Tile %s on level %s contains the point %s",
        tileId, ZOOM_LEVEL_TWELVE, geoCoordinateSanFrancisco);
  }
}

To execute the application, run the following command:

Scala
Java

mvn package exec:java -D"exec.mainClass"="HereTileWithPointTutorialScala"


mvn package exec:java -D"exec.mainClass"="HereTileWithPointTutorial"

The result of the following application is:

Tile 19319036 on level 12 contains the point GeoCoordinate(37.82171,-122.47881)

In the previous tutorial, we learned how to find a Tile for a certain coordinate on the map. This tutorial describes how to find Tiles that cover a certain area on the map. To get Tiles in a certain rectangular part of the map, you should use Bounding Box.

The fromBoundingBox(boundingBox) method in the [ Java API | Scala API ] class, allows you to get Tiles that cover a given bounding box.

The following application uses the Golden Gate Park longitudes and latitudes:

  • Westbound Longitude = -122.511241
  • Eastbound Longitude = -122.452919
  • Southbound Latitude = 37.763901
  • Northbound Latitude = 37.774723

and returns a set of Tile IDs that cover Golden Gate Park on the zoom level of 15.

The following figure shows a bounding box with a red border and Tiles that cover this bbox with the blue borders.

Tiles that cover `Golder Gate Park` in San Francisco
Figure 5. Tiles that cover `Golder Gate Park` in San Francisco

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.BoundingBox
import com.here.platform.location.inmemory.geospatial.TileId
import com.here.platform.location.integration.herecommons.geospatial.{
  HereTileLevel,
  HereTileResolver
}

object HereBoundingBoxTutorialScala {

  val ZOOM_LEVEL_FIFTEEN = 15

  def main(args: Array[String]): Unit = {

    val westBoundLongitude = -122.511241
    val eastBoundLongitude = -122.452919
    val southBoundLatitude = 37.763901
    val northBoundLatitude = 37.774723

    val hereTileResolver = new HereTileResolver(HereTileLevel(ZOOM_LEVEL_FIFTEEN))

    val bbox = new BoundingBox(northBoundLatitude,
                               southBoundLatitude,
                               eastBoundLongitude,
                               westBoundLongitude)

    val tilesCoveringBbox: Set[TileId] = hereTileResolver.fromBoundingBox(bbox)

    println(
      s"Tiles covering $bbox on level $ZOOM_LEVEL_FIFTEEN are [${tilesCoveringBbox.map(_.value).mkString(", ")}]")
  }
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.BoundingBox;
import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel;
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver;
import java.util.Set;

public class HereBoundingBoxTutorial {

  public static final int ZOOM_LEVEL_FIFTEEN = 15;

  public static void main(String[] args) {

    double westBoundLongitude = -122.511241;
    double eastBoundLongitude = -122.452919;
    double southBoundLatitude = 37.763901;
    double northBoundLatitude = 37.774723;

    HereTileResolver hereTileResolver = new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_FIFTEEN));

    BoundingBox bbox =
        new BoundingBox(
            northBoundLatitude, southBoundLatitude, eastBoundLongitude, westBoundLongitude);

    Set<Long> tilesCoveringBbox = hereTileResolver.fromBoundingBox(bbox);

    System.out.printf(
        "Tiles covering %s on level %s are %s", bbox, ZOOM_LEVEL_FIFTEEN, tilesCoveringBbox);
  }
}

To execute the application, run the following command:

Scala
Java

mvn package exec:java -D"exec.mainClass"="HereBoundingBoxTutorialScala"


mvn package exec:java -D"exec.mainClass"="HereBoundingBoxTutorial"

The result of the following application is:

Tiles covering BoundingBox(37.774723,37.763901,-122.452919,-122.511241) on level 15 are [1236417959, 1236417970, 1236417977, 1236417971, 1236417974, 1236417954, 1236417955, 1236417976, 1236417958, 1236417964, 1236417960, 1236417980, 1236417965, 1236417961]

The previous tutorial demonstrates how to get Tiles in a certain rectangular part of the map. However, the HereTileResolver class provides the fromCenterAndRadius(centerPoint, radius) [ Java API | Scala API ] method that allows you to find Tiles in a certain circle with the specified center and radius in meters. This method can be useful if you do not have the exact coordinates to create a bounding box, but you do have a specific point on the map. In this case, to get all the Tiles that cover this circle, you can specify this point, and the radius in meters.

The following application in both Java and Scala returns Tiles that cover the circle with geocoordinates of Holly Park in San Francisco and radius 150 meters using zoom level 18. Geo Coordinate instance is created using Holly Park Latitude 37.74638 and Holy Park Longitude -122.43584.

The following figure shows a circle with a radius of 150 meters which covers Holy Park and four Tiles that cover this circle.

Tiles that cover `Holly Park` in San Francisco.
Figure 6. Tiles that cover `Holly Park` in San Francisco.

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.GeoCoordinate
import com.here.platform.location.inmemory.geospatial.TileId
import com.here.platform.location.integration.herecommons.geospatial.{
  HereTileLevel,
  HereTileResolver
}

object HereTileRadiusSearchScala {

  val ZOOM_LEVEL_EIGHTEEN = 18

  def main(args: Array[String]): Unit = {

    val sanFranciscoLatitude = 37.73721
    val sanFranciscoLongitude = -122.41994
    val radiusInMeters = 100

    val hereTileResolver = new HereTileResolver(HereTileLevel(ZOOM_LEVEL_EIGHTEEN))

    val sanFranciscoGeoCoordinates = new GeoCoordinate(sanFranciscoLatitude, sanFranciscoLongitude)

    val tilesCoveringCircle: Set[TileId] =
      hereTileResolver.fromCenterAndRadius(sanFranciscoGeoCoordinates, radiusInMeters)

    println(
      s"A circle with a center at $sanFranciscoGeoCoordinates and radius of $radiusInMeters meters covers the following tiles on level $ZOOM_LEVEL_EIGHTEEN: ${tilesCoveringCircle
        .map(_.value)
        .mkString(", ")}")

  }
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.GeoCoordinate;
import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel;
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver;
import java.util.Set;

public class HereTileRadiusSearchTutorial {

  public static final int ZOOM_LEVEL_EIGHTEEN = 18;

  public static void main(String[] args) {

    double sanFranciscoLatitude = 37.73721;
    double sanFranciscoLongitude = -122.41994;
    double radiusInMeters = 100;

    HereTileResolver hereTileResolver =
        new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_EIGHTEEN));

    GeoCoordinate geoCoordinateSanFrancisco =
        new GeoCoordinate(sanFranciscoLatitude, sanFranciscoLongitude);

    Set<Long> tilesCoveringCircle =
        hereTileResolver.fromCenterAndRadius(geoCoordinateSanFrancisco, radiusInMeters);

    System.out.printf(
        "A circle with a center at %s and radius of %s meters covers the following tiles on level %s: %s",
        geoCoordinateSanFrancisco, radiusInMeters, ZOOM_LEVEL_EIGHTEEN, tilesCoveringCircle);
  }
}

To execute the application, run the following command:

Scala
Java

mvn package exec:java -D"exec.mainClass"="HereTileRadiusSearchScala"


mvn package exec:java -D"exec.mainClass"="HereTileRadiusSearchTutorial"

The result of the following application is:

A circle with a center at 37.73721,-122.41994 and radius of 100.0 meters covers the following tiles on level 18: [79130751637, 79130751551, 79130751592, 79130751680, 79130751594, 79130751681, 79130751549, 79130751595, 79130751593]

Interfacing with other libraries using the GeoCoordinate Adapter

When working with the HereTileResolver class, you often have to create a GeoCoordinate instance to transfer coordinates on a map as we have done many times in the above code sections. Location Library provides the GeoCoordinate class that implements the GeoCoordinateHolder interface. It is highly recommended to use the GeoCoordinate class from the Location Library, however, if you choose not to, you should consider whether your class implements the GeoCoordinateHolder interface if you want to use the instance of this class to work with Location Library. If your class doesn't implement the GeoCoordinateHolder interface, as in this tutorial - then you need to provide a GeoCoordinateAdapter adapter.

This tutorial uses two custom GeoCoordinateJava and GeoCoordinateScala classes that do not implement GeoCoordinateHolder interface:

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

case class GeoCoordinateScala(latitude: Double, longitude: Double) {
  def getLatitude: Double = latitude
  def getLongitude: Double = longitude
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

public class GeoCoordinateJava {
  private double latitude;
  private double longitude;

  public GeoCoordinateJava(double latitude, double longitude) {
    this.latitude = latitude;
    this.longitude = longitude;
  }

  public double getLatitude() {
    return latitude;
  }

  public double getLongitude() {
    return longitude;
  }

  @Override
  public String toString() {
    return String.format("(%s,%s)", latitude, longitude);
  }
}

and two custom GeoCoordinateAdapterJava and GeoCoordinateAdapterScala adapters that implement the GeoCoordinateAdapter adapter from the Location Library:

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.javadsl.GeoCoordinateAdapter

class GeoCoordinateAdapterScala private extends GeoCoordinateAdapter[GeoCoordinateScala] {
  override def getLatitude(instance: GeoCoordinateScala): Double = instance.latitude
  override def getLongitude(instance: GeoCoordinateScala): Double = instance.longitude
}

object GeoCoordinateAdapterScala {
  lazy val getInstance = new GeoCoordinateAdapterScala()
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.javadsl.GeoCoordinateAdapter;

public class GeoCoordinateAdapterJava implements GeoCoordinateAdapter<GeoCoordinateJava> {

  private GeoCoordinateAdapterJava() {}

  @Override
  public double getLatitude(GeoCoordinateJava instance) {
    return instance.getLatitude();
  }

  @Override
  public double getLongitude(GeoCoordinateJava instance) {
    return instance.getLongitude();
  }

  public static GeoCoordinateAdapterJava getInstance() {
    return new GeoCoordinateAdapterJava();
  }
}

The following tutorial demonstrates the same as previous one but with the use of custom GeoCoordinatesJava and GeoCoordinatesScala classes that do not implement GeoCoordinateHolder interface, and custom GeoCoordinateAdapterJava and GeoCoordinateAdapterScala adapters that implement GeoCoordinateAdapter interface.

Scala
Java
/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.javadsl.GeoCoordinateAdapter
import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver

import java.{lang, util};

object HereTileRadiusSearchWithGcAdapterTutorialScala {

  val ZOOM_LEVEL_EIGHTEEN = 18

  def main(args: Array[String]): Unit = {

    val sanFranciscoLatitude = 37.73721
    val sanFranciscoLongitude = -122.41994
    val radiusInMeters = 100

    val hereTileResolver = new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_EIGHTEEN))

    val gca: GeoCoordinateAdapter[GeoCoordinateScala] = GeoCoordinateAdapterScala.getInstance
    val geoCoordinateSanFrancisco: GeoCoordinateScala =
      GeoCoordinateScala(sanFranciscoLatitude, sanFranciscoLongitude)
    val tilesCoveringCircle: util.Set[lang.Long] =
      hereTileResolver.fromCenterAndRadius(geoCoordinateSanFrancisco, radiusInMeters)(gca)

    println(
      s"A circle with a center at $geoCoordinateSanFrancisco and radius of $radiusInMeters meters covers the following tiles on level $ZOOM_LEVEL_EIGHTEEN: $tilesCoveringCircle")
  }
}

/*
 * Copyright (c) 2018-2023 HERE Europe B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.here.platform.location.core.geospatial.javadsl.GeoCoordinateAdapter;
import com.here.platform.location.integration.herecommons.geospatial.HereTileLevel;
import com.here.platform.location.integration.herecommons.geospatial.javadsl.HereTileResolver;
import java.util.Set;

public class HereTileRadiusSearchWithGcAdapterTutorial {

  public static final int ZOOM_LEVEL_EIGHTEEN = 18;

  public static void main(String[] args) {

    double sanFranciscoLatitude = 37.73721;
    double sanFranciscoLongitude = -122.41994;
    double radiusInMeters = 100;

    HereTileResolver hereTileResolver =
        new HereTileResolver(new HereTileLevel(ZOOM_LEVEL_EIGHTEEN));

    GeoCoordinateAdapter<GeoCoordinateJava> gca = GeoCoordinateAdapterJava.getInstance();
    GeoCoordinateJava geoCoordinateSanFrancisco =
        new GeoCoordinateJava(sanFranciscoLatitude, sanFranciscoLongitude);
    Set<Long> tilesCoveringCircle =
        hereTileResolver.fromCenterAndRadius(geoCoordinateSanFrancisco, radiusInMeters, gca);

    System.out.printf(
        "A circle with a center at %s and radius of %s meters covers the following tiles on level %s: %s",
        geoCoordinateSanFrancisco, radiusInMeters, ZOOM_LEVEL_EIGHTEEN, tilesCoveringCircle);
  }
}

To execute the application, run the following command:

Scala
Java

mvn package exec:java -D"exec.mainClass"="HereTileRadiusSearchWithGcAdapterTutorialScala"


mvn package exec:java -D"exec.mainClass"="HereTileRadiusSearchWithGcAdapterTutorial"

The result of the following application is:

A circle with a center at 37.73721,-122.41994 and radius of 100.0 meters covers the following tiles on level 18: [79130751637, 79130751551, 79130751592, 79130751680, 79130751594, 79130751681, 79130751549, 79130751595, 79130751593]

Further information

For more details on the topics covered in this tutorial, see the following sources:

results matching ""

    No results matching ""