Platform Data Extension
Platform Data Extension (PDE) provides the ability to easily access Platform Data Extension API from SDK for Android. You can use this extension to access a wide range of data that can be later used for different use cases. Some examples include displaying road elevation, slopes, and traffic signs. For more information about use cases and the types of data that can be accessed through PDE, check Platform Data Extension API Developer's Guide .
PDE Thematic Layers
PDE divides map content across many thematic layers. Each thematic data layer serves a specific use case and only contains the data required for it such as road elevation. To use PDE, you need to first decide on the required data for your app and select the PDE thematic layers accordingly. The available thematic layers can be found via PDE Layers API. You can then check the targeted thematic layers via the individual Layer API. Before starting to use PDE in your app you need to select the correct thematic layers and then decide on what data to use and how to use it. This is a crucial step.
_FCx
" where x
is a number from 1 to 5. For example, ROAD_GEOM_FC1
. If a layer isn't related to road links, e.g. PSTLCB_GEN
layer, you don't need to append "_FCx
" suffix and specify the tile layer. PDE Classes
Class | Description |
---|---|
PlatformDataRequest | Used to create and execute a PDE data request with the specified layers and GeoBoundingBox object. |
PlatformDataResult | The result of running a PDE data request. Contains data for requested layers which are accessible by the layer name. This implements java.util.Map interface for ease of use and interoperability, although objects of this class are unmodifiable. Provides extract method to convert the result into a Map<String, List<Map<String, String>>> which is a raw representation of the underlying data. |
PlatformDataItemCollection | Represents layer data in the form of an array of PlatformDataItem objects. This is what you get by asking PlatformDataResult for a specific layer — for example, by calling platformDataResult.get("ROAD_GEOM_FC1") . PlatformDataItemCollection implements java.util.List interface for ease of use and interoperability, although objects of this class are unmodifiable. It also provides an extract() method to convert the collection into a List<Map<String, String>> object. |
PlatformDataItem | Represents actual data records in a layer as a map of attribute names and values. This is what you get from PlatformDataItemCollection by iterating over its contents or asking for an item at a specific index. Note that for several attributes shortcut methods are provided, such as item.getLinkId() which can be used instead of item.get("LINK_ID") . This implements java.util.Map interface for ease of use and interoperability, although objects of this class are unmodifiable. Provides extract() method to convert it into a Map<String, String> object. |
In summary, PlatformDataRequest
produces PlatformDataResult
, a Map
of layer names to PlatformDataItemCollection
objects, which in turn are List
of PlatformDataItem
objects, with each containing attribute names and their values in the form of a Map
.
Example: Requesting the PDE Data
The example below shows how a feature is implemented using the PDE data. The goal is to color each road segment according to its average height. For this feature you need the PDE data from ROAD_GEOM_FC1
and BASIC_HEIGHT_FC1
layers. As of now the only way to request the PDE data requires the layers specified with a GeoBoundingBox
.
ROAD_GEOM_FC[number]
and BASIC_HEIGHT_FC[number]
layers are also referred to as "tile" layers since their data is split into multiple tiles. Due to server limitations up to 15 tiles can be requested at a time. PDE also supports non-tile layers. To use the data from both layers, you need to join the two layers by using LINK_ID
property. This is demonstrated in the next section.

The following are sample results from the Layer request.
ROAD_GEOM_FC1
Layer API result:
{
"description": "Ungeneralized road, ferry and rail ferry geometry (polylines).<br/>If a road link crosses a tile boundary, it will be written into each of the tiles, each including the full link geometry. This simplifies use cases other than pure display of all geometry within a rectangle.",
"attributes": {
"LINK_ID": "Permanent link ID. Positive 64 bit Integer that globally identifies the road, carto or buildin footprint link, also across map releases. Link IDs are never reused.",
"LONG_HAUL": "This link or polygon or POI is of major importance. It should be displayed at high zoom levels, and it should be included for routing in/through regions where no detailed routing is supported.",
"NAME": "A name of this road line. Roads can have multiple names, in the same or multiple languages. This field contains any of those.",
"NAMES": "List of all names for this object, in all languages [...]",
"TUNNEL": "Is this navigable link or railroad a tunnel?",
"BRIDGE": "Is this navigable link or railroad a bridge?",
"LAT": "Latitude coordinates [10^-5 degree WGS84] along the polyline. ",
"LON": "Longitude coordinates [10^-5 degree WGS84] along the polyline. ",
"ZLEVEL": "(-4 ... 11) indicates the height of the point relative to another point on a grade separated crossing with any other line. Comma separated. If z-level is null then the value '0' is left out."
},
"referencedStaticContents": [],
"tileRequestsLevel": 9,
"tileX": 499,
"tileY": 403,
"isStaticContent": false
}
BASIC_HEIGHT_FC1
Layer API result:
{
"description": "Link height values computed from a Digital Terrain Model, cleaned up for continuity along links, bridges and tunnels. Less accurate than ADAS link height values, but full coverage and sufficient for certain use cases.",
"attributes": {
"LINK_ID": "Permanent link ID. Positive 64 bit Integer that globally identifies the road, carto or building footprint link, also across map releases. Link IDs are never reused.",
"DTM_MIN_HEIGHT": "The minimum height [cm above WGS84 ellipsoid] encountered along the link.",
"DTM_MAX_HEIGHT": "The maximum height [cm above WGS84 ellipsoid] encountered along the link.",
"DTM_AVG_HEIGHT": "The average height [cm above WGS84 ellipsoid] along the link.",
"DTM_REF_ZCOORD": "Height [cm above WGS84 ellipsoid] at the reference node of the link.",
"DTM_NONREF_ZCOORD": "Height [cm above WGS84 ellipsoid] at the non-reference node of the link."
},
"referencedStaticContents": [],
"tileRequestsLevel": 9,
"tileX": 496,
"tileY": 358,
"isStaticContent": false
}
To begin using the PDE layers, create a PlatformDataRequest
object:
Set<String> layers = new HashSet<String>(Arrays.asList("LINK_FC1", "BASIC_HEIGHT_FC1"));
GeoBoundingBox box = mapView.getBoundingBox();
PlatformDataRequest request =
PlatformDataRequest.createBoundingBoxRequest(RoadElevationProcessor.LAYERS, box);
Note that trying to create a request with invalid parameters causes a java.lang.IllegalArgumentException
.
Next, you need to supply a Listener<PlatformDataResult>
object to get the results of the request.
request.execute(new PlatformDataRequest.Listener<PlatformDataResult>() {
@Override
public void onCompleted(PlatformDataResult data, PlatformDataRequest.Error error) {
if (error != null) {
Log.w(TAG, "PlatformDataRequest failed with error: " + error);
} else {
// process received data
RoadElevationProcessor processor = new RoadElevationProcessor(data);
processor.process(new Listener<List<MapObject>>() {
@Override
public void onResult(List<MapObject> result) {
m_elevationPolylines = result;
m_elevationLegend.setVisibility(View.VISIBLE);
m_map.addMapObjects(m_elevationPolylines);
onRequestEnd(null);
}
});
}
}
});
Example: Processing the PDE Data
After the data result is successfully retrieved, you need to process the data. In the example above you have asked for ROAD_GEOM_FC1
and BASIC_HEIGHT_FC1
data restricted with the map view bounding box. For tutorial purpose we then implemented a sample data processor class, PlatformDataProcessor
, which provides methods to join the layers and can be extended to consume the joined data. We use these classes to implement the color feature.
public abstract class PlatformDataProcessor<T> {
protected final PlatformDataResult m_data;
private Handler m_handler = new Handler(Looper.getMainLooper());
public PlatformDataProcessor(PlatformDataResult data) {
m_data = data;
}
public void process(final Listener<T> listener) {
new Thread(new Runnable() {
@Override
public void run() {
final T result = doProcess();
m_handler.post(new Runnable() {
@Override
public void run() {
listener.onResult(result);
}
});
}
}).start();
}
protected abstract T doProcess();
protected Map<String, PlatformDataItem> map(Indexer indexer, PlatformDataItemCollection items) {
Map<String, PlatformDataItem> ret = new HashMap<String, PlatformDataItem>();
map(indexer, items, ret);
return ret;
}
protected void map(Indexer indexer, PlatformDataItemCollection items,
Map<String, PlatformDataItem> output) {
for (PlatformDataItem item : items) {
output.put(indexer.getIndexValue(item), item);
}
}
protected Map<String, List<PlatformDataItem>> multimap(MultiIndexer indexer,
PlatformDataItemCollection items) {
Map<String, List<PlatformDataItem>> ret = new HashMap<String, List<PlatformDataItem>>();
multimap(indexer, items, ret);
return ret;
}
protected void multimap(MultiIndexer indexer, PlatformDataItemCollection items,
Map<String, List<PlatformDataItem>> output) {
for (PlatformDataItem item : items) {
for (String value : indexer.getIndexValue(item)) {
List<PlatformDataItem> entry = output.get(value);
if (entry == null) {
entry = new ArrayList<PlatformDataItem>();
output.put(value, entry);
}
entry.add(item);
}
}
}
protected Map<PlatformDataItem, PlatformDataItem> join(Indexer commonIndexer,
PlatformDataItemCollection mainLayer, PlatformDataItemCollection otherLayer) {
return join(commonIndexer, mainLayer, commonIndexer, otherLayer);
}
protected Map<PlatformDataItem, PlatformDataItem> join(Indexer mainIndexer,
PlatformDataItemCollection mainLayer, Indexer otherIndexer,
PlatformDataItemCollection otherLayer) {
Map<PlatformDataItem, PlatformDataItem> ret =
new HashMap<PlatformDataItem, PlatformDataItem>();
Map<String, PlatformDataItem> mappedItems = map(otherIndexer, otherLayer);
for (PlatformDataItem item : mainLayer) {
String value = mainIndexer.getIndexValue(item);
PlatformDataItem matchedItem = mappedItems.get(value);
ret.put(item, matchedItem);
}
return ret;
}
protected Map<PlatformDataItem, List<PlatformDataItem>> join(MultiIndexer mainIndexer,
PlatformDataItemCollection mainLayer, PlatformDataItemCollection otherLayer) {
return join(mainIndexer, mainLayer, mainIndexer, otherLayer);
}
protected Map<PlatformDataItem, List<PlatformDataItem>> join(MultiIndexer mainIndexer,
PlatformDataItemCollection mainLayer, MultiIndexer otherIndexer,
PlatformDataItemCollection otherLayer) {
Map<String, List<PlatformDataItem>> mappedItems = multimap(otherIndexer, otherLayer);
Map<PlatformDataItem, List<PlatformDataItem>> ret =
new HashMap<PlatformDataItem, List<PlatformDataItem>>();
for (PlatformDataItem item : mainLayer) {
for (String value : mainIndexer.getIndexValue(item)) {
List<PlatformDataItem> matchedItems = mappedItems.get(value);
List<PlatformDataItem> entry = ret.get(item);
if (entry == null) {
entry = new ArrayList<PlatformDataItem>();
ret.put(item, entry);
}
if (matchedItems != null) {
entry.addAll(matchedItems);
}
}
}
return ret;
}
interface Indexer {
String getIndexValue(PlatformDataItem item);
}
interface MultiIndexer {
String[] getIndexValue(PlatformDataItem item);
}
interface Listener<T> {
void onResult(T result);
}
}
Note that PlatformDataProcessor.join()
method is for inner joining the thematic layers with properties where more than one data item is possible. For example, TRAFFIC_SIGN_FCx
thematic layer has LINK_IDS
property where as much as two LINK_ID
s are contained.
Next, create RoadElevationProcessor
. RoadElevationProcessor
class extends PlatformDataProcessor
for coloring the road segments according to their average height. This class needs the result and a dedicated map container intended for the MapPolyline
objects. Note that the base class PlatformDataProcessor
performs inner joining of thematic layer data depending on the indexer blocks provided. RoadElevationProcessor
class utilizes the joined data to implement the intended feature.
public class RoadElevationProcessor extends PlatformDataProcessor<List<MapObject>> {
public static final String ROAD_GEOM_LAYER = "ROAD_GEOM_FC1";
public static final String BASIC_HEIGHT_LAYER = "BASIC_HEIGHT_FC1";
public static final Set<String> LAYERS = new HashSet<String>(Arrays.asList(ROAD_GEOM_LAYER,
BASIC_HEIGHT_LAYER));
public RoadElevationProcessor(PlatformDataResult data) {
super(data);
}
protected List<MapObject> doProcess() {
List<MapObject> polylines = new ArrayList<MapObject>();
PlatformDataItemCollection roadGeomLayer = m_data.get(ROAD_GEOM_LAYER);
PlatformDataItemCollection basicHeightLayer = m_data.get(BASIC_HEIGHT_LAYER);
Map<PlatformDataItem, PlatformDataItem> combinedData =
join(LINK_ID_INDEXER, roadGeomLayer, basicHeightLayer);
for (Map.Entry<PlatformDataItem, PlatformDataItem> entry : combinedData.entrySet()) {
PlatformDataItem roadGeomItem = entry.getKey();
PlatformDataItem basicHeightItem = entry.getValue();
List<GeoCoordinate> linkGeometry = roadGeomItem.getCoordinates();
if (linkGeometry.size() > 1) {
GeoPolyline geoPolyline = new GeoPolyline(linkGeometry);
MapPolyline mapPolyline = new MapPolyline(geoPolyline);
mapPolyline.setLineColor(calculateColor(basicHeightItem.getAverageHeight()));
mapPolyline.setLineWidth(10);
polylines.add(mapPolyline);
}
}
return polylines;
}
private int calculateColor(int height) {
if (height > 75000) {
return 0xff00695c;
} else if (height > 25000) {
return 0xff009688;
} else if (height > 0) {
return 0xff4db6ac;
} else if (height > -25000) {
return 0xff7986cb;
} else if (height > -75000) {
return 0xff5c6bc0;
} else {
return 0xff3f51b5;
}
}
private Indexer LINK_ID_INDEXER = new Indexer() {
@Override
public String getIndexValue(PlatformDataItem item) {
return item.getLinkId();
}
};
}
The main method for RoadElevationProcessor
class is doProcess()
method.
The first thing this method does is joining the data coming from two different thematic layers with the "indexer" blocks. Here the data is joined via LINK_ID
's. Note that linkId
property is available as a shortcut property in PlatformDataItem
class. After joining the data each ROAD_GEOM_FC1
data item is inner joined with the relevant BASIC_HEIGHT_FC1
data item, and the joined data is returned as a dictionary where the actual type is Map<PlatformDataItem, PlatformDataItem>
. Each key-value pair provides the necessary data for the feature: the key of PlatformDataItem
type representing ROAD_GEOM_FC1
thematic layer data provides the road geometry such as coordinates of the road segment, and the value of PlatformDataItem
type representing BASIC_HEIGHT_FC1
thematic layer data provides the average height of that road segment. The joined data provides all the data required for implementing the intended feature.
{
BRIDGE = N;
LAT = "5246124,2";
"LINK_ID" = 936938339;
LON = "1342560,24";
"LONG_HAUL" = Y;
NAME = A100;
NAMES = "GERBNTunnel BritzGERY\"tU|n@l \"brItsGERBNA100GERN\"?a: \"hUn|d6t;GERN\"?aU|to:|ba:n ?aIn|\"hUn|d6t;GERY\"?a: ?aIn|\"hUn|d6t;GERN\"?aU|to:|ba:n \"hUn|d6tGERBNStadtring BerlinGERY\"Stat|rIN bEr|\"li:n";
TUNNEL = Y;
ZLEVEL = ",";
} = {
"DTM_AVG_HEIGHT" = 8500;
"DTM_MAX_HEIGHT" = 8693;
"DTM_MIN_HEIGHT" = 8099;
"DTM_NONREF_ZCOORD" = 8500;
"DTM_REF_ZCOORD" = 8500;
"LINK_ID" = 936938339;
}
Note that the key-value pair has the same LINK_ID
.