Skip to content
This repository has been archived by the owner on Mar 8, 2023. It is now read-only.

Commit

Permalink
HARP-12959 Support GeoJSON point features with altitude for pre-tiled…
Browse files Browse the repository at this point in the history
… data. (#1973)

* HARP-12959: Pass GeoJSON altitude along from GeoJsonDataAdapter to decoder.

* HARP-12959: Add example with elevated markers.

* HARP-12959: Add IBCT test.

* HARP-12959: Address review comments.
  • Loading branch information
atomicsulfate committed Nov 19, 2020
1 parent 5fd6d51 commit 2183218
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 62 deletions.
142 changes: 142 additions & 0 deletions @here/harp-examples/src/datasource_geojson_elevated-markers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright (C) 2017-2020 HERE Europe B.V.
* Licensed under Apache 2.0, see full license in LICENSE
* SPDX-License-Identifier: Apache-2.0
*/

import { GeoCoordinates, TileKey } from "@here/harp-geoutils";
import { MapControls, MapControlsUI } from "@here/harp-map-controls";
import { CopyrightElementHandler, MapView } from "@here/harp-mapview";
import { DataProvider } from "@here/harp-mapview-decoder";
import {
APIFormat,
AuthenticationMethod,
VectorTileDataSource
} from "@here/harp-vectortile-datasource";

import { apikey, copyrightInfo } from "../config";

/**
* This examples shows how to render elevated GeoJSON points as markers.
*/
export namespace ElevatedGeoJsonMarkersExample {
// Implement a class extending [[DataProvider]] that will generate the GeoJSON features.
class CustomGeoJsonDataProvider extends DataProvider {
connect() {
// Here you could connect to the service.
return Promise.resolve();
}

ready() {
// Return true if connect was successful.
return true;
}

getTile(tileKey: TileKey, abortSignal?: AbortSignal): Promise<{}> {
// Return a GeoJSON FeatureCollection with features in the tile with given `tileKey`.
return Promise.resolve({
type: "FeatureCollection",
features: [
{
type: "Feature",
id: 0,
properties: { text: "Fernsehturm" },
geometry: { type: "Point", coordinates: [13.4094, 52.52085, 420] }
}
]
});
}

/** @override */ dispose() {
// Nothing to be done here.
}
}

/**
* Creates a new MapView for the HTMLCanvasElement of the given id.
*/
function initializeMapView(id: string): MapView {
const canvas = document.getElementById(id) as HTMLCanvasElement;
const imageTexture = "custom-icon";
const map = new MapView({
canvas,
theme: {
extends: "resources/berlin_tilezen_base.json",
styles: {
// Specify the styling for the markers.
geojson: [
{
when: ["==", ["geometry-type"], "Point"],
technique: "labeled-icon",
imageTexture,
text: ["get", "text"],
iconYOffset: 35,
size: 15,
priority: 1000
}
]
}
},
target: new GeoCoordinates(52.5237, 13.4089),
zoomLevel: 17.4,
tilt: 78
});

// Register the icon image referenced in the style.
// tslint:disable-next-line:max-line-length
const imageString =
"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIyLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHdpZHRoPSI0OHB4IiBoZWlnaHQ9IjQ4cHgiIHZlcnNpb249IjEuMSIgaWQ9Imx1aS1pY29uLWRlc3RpbmF0aW9ucGluLW9uZGFyay1zb2xpZC1sYXJnZSIKCSB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ4IDQ4IgoJIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQ4IDQ4IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPGc+Cgk8ZyBpZD0ibHVpLWljb24tZGVzdGluYXRpb25waW4tb25kYXJrLXNvbGlkLWxhcmdlLWJvdW5kaW5nLWJveCIgb3BhY2l0eT0iMCI+CgkJPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTQ3LDF2NDZIMVYxSDQ3IE00OCwwSDB2NDhoNDhWMEw0OCwweiIvPgoJPC9nPgoJPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmZmZmYiIGQ9Ik0yNCwyQzEzLjg3MDgsMiw1LjY2NjcsMTAuMTU4NCw1LjY2NjcsMjAuMjIzMwoJCWMwLDUuMDMyNSwyLjA1MzMsOS41ODg0LDUuMzcxNywxMi44ODgzTDI0LDQ2bDEyLjk2MTctMTIuODg4M2MzLjMxODMtMy4zLDUuMzcxNy03Ljg1NTgsNS4zNzE3LTEyLjg4ODMKCQlDNDIuMzMzMywxMC4xNTg0LDM0LjEyOTIsMiwyNCwyeiBNMjQsMjVjLTIuNzY1LDAtNS0yLjIzNS01LTVzMi4yMzUtNSw1LTVzNSwyLjIzNSw1LDVTMjYuNzY1LDI1LDI0LDI1eiIvPgo8L2c+Cjwvc3ZnPgo=";
map.userImageCache.addImage(imageTexture, imageString);

CopyrightElementHandler.install("copyrightNotice").attach(map);

const controls = new MapControls(map);

// Add an UI.
const ui = new MapControlsUI(controls, { projectionSwitch: true, zoomLevel: "input" });
canvas.parentElement!.appendChild(ui.domElement);

window.addEventListener("resize", () => {
map.resize(window.innerWidth, window.innerHeight);
});

// Create a [[VectorTileDataSource]] using the custom data provider and the name of the
// style set for the markers.
map.addDataSource(
new VectorTileDataSource({
dataProvider: new CustomGeoJsonDataProvider(),
name: "geojson",
styleSetName: "geojson"
})
);

map.update();

return map;
}

document.body.innerHTML += `
<style>
#mapCanvas {
top: 0;
}
</style>
`;

const mapView = initializeMapView("mapCanvas");

const omvDataSource = new VectorTileDataSource({
baseUrl: "https://vector.hereapi.com/v2/vectortiles/base/mc",
apiFormat: APIFormat.XYZOMV,
styleSetName: "tilezen",
maxDataLevel: 17,
authenticationCode: apikey,
authenticationMethod: {
method: AuthenticationMethod.QueryString,
name: "apikey"
},
copyrightInfo
});

mapView.addDataSource(omvDataSource);
}
1 change: 1 addition & 0 deletions @here/harp-geoutils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export * from "./lib/tiling/HereTilingScheme";
export * from "./lib/tiling/WebMercatorTilingScheme";
export * from "./lib/tiling/MercatorTilingScheme";
export * from "./lib/tiling/PolarTilingScheme";
export * from "./lib/math/Vector2Like";
export * from "./lib/math/Vector3Like";
export * from "./lib/math/Box3Like";
export * from "./lib/math/OrientedBox3Like";
Expand Down
24 changes: 24 additions & 0 deletions @here/harp-geoutils/lib/math/Vector2Like.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2017-2020 HERE Europe B.V.
* Licensed under Apache 2.0, see full license in LICENSE
* SPDX-License-Identifier: Apache-2.0
*/

/**
* Interface representing a Vector2.
*/
export interface Vector2Like {
/**
* The X position.
*/
x: number;

/**
* The Y position.
*/
y: number;
}

export function isVector2Like(v: any): v is Vector2Like {
return v && typeof v.x === "number" && typeof v.y === "number";
}
6 changes: 3 additions & 3 deletions @here/harp-vectortile-datasource/lib/IGeometryProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { MapEnv } from "@here/harp-datasource-protocol/index-decoder";
import { GeoCoordinates } from "@here/harp-geoutils";
import { Vector2 } from "three";
import { Vector2, Vector3 } from "three";

/**
* An interface to represent polygon geometries.
Expand Down Expand Up @@ -69,14 +69,14 @@ export interface IGeometryProcessor {
*
* @param layerName - The name of the enclosing layer.
* @param layerExtents - The extents of the enclosing layer.
* @param geometry - The positions of the point features in the projected world coordinates.
* @param geometry - The positions of the point features in local webMercator coordinates.
* @param env - The environment containing the properties of the geometry.
* @param storageLevel - The storage level of the data.
*/
processPointFeature(
layerName: string,
layerExtents: number,
geometry: Vector2[],
geometry: Vector3[],
env: MapEnv,
storageLevel: number
): void;
Expand Down
52 changes: 29 additions & 23 deletions @here/harp-vectortile-datasource/lib/OmvUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
* Licensed under Apache 2.0, see full license in LICENSE
* SPDX-License-Identifier: Apache-2.0
*/
import { EarthConstants, webMercatorProjection } from "@here/harp-geoutils";
import {
EarthConstants,
isVector3Like,
Vector2Like,
Vector3Like,
webMercatorProjection
} from "@here/harp-geoutils";
import * as THREE from "three";

import { DecodeInfo } from "./DecodeInfo";
Expand Down Expand Up @@ -62,13 +68,13 @@ export function createWorldTileTransformationCookie(extents: number, decodeInfo:
/**
* @hidden
*/
export function tile2world(
export function tile2world<VectorType extends Vector3Like>(
extents: number,
decodeInfo: DecodeInfo,
position: THREE.Vector2,
position: Vector2Like,
flipY: boolean = false,
target: THREE.Vector2
): THREE.Vector2 {
target: VectorType
): VectorType {
if (
decodeInfo.worldTileProjectionCookie === undefined ||
decodeInfo.worldTileProjectionCookie.extents !== extents
Expand All @@ -82,22 +88,23 @@ export function tile2world(
const { top, left, scale } = decodeInfo.worldTileProjectionCookie;
const R = EarthConstants.EQUATORIAL_CIRCUMFERENCE;

return target.set(
((left + position.x) / scale) * R,
((top + (flipY ? -position.y : position.y)) / scale) * R
);
target.x = ((left + position.x) / scale) * R;
target.y = ((top + (flipY ? -position.y : position.y)) / scale) * R;
target.z = isVector3Like(position) ? position.z : 0;

return target;
}

/**
* @hidden
*/
export function world2tile(
export function world2tile<VectorType extends Vector2Like>(
extents: number,
decodeInfo: DecodeInfo,
position: THREE.Vector2,
position: Vector3Like,
flipY: boolean = false,
target: THREE.Vector2
): THREE.Vector2 {
target: VectorType
): VectorType {
if (
decodeInfo.worldTileProjectionCookie === undefined ||
decodeInfo.worldTileProjectionCookie.extents !== extents
Expand All @@ -110,30 +117,29 @@ export function world2tile(
const { top, left, scale } = decodeInfo.worldTileProjectionCookie;
const R = EarthConstants.EQUATORIAL_CIRCUMFERENCE;

return target.set(
(position.x / R) * scale - left,
(flipY ? -1 : 1) * ((position.y / R) * scale - top)
);
target.x = (position.x / R) * scale - left;
target.y = (flipY ? -1 : 1) * ((position.y / R) * scale - top);
if (isVector3Like(target)) {
target.z = position.z;
}
return target;
}

const tempWorldPos = new THREE.Vector2();

export function webMercatorTile2TargetWorld(
extents: number,
decodeInfo: DecodeInfo,
position: THREE.Vector2,
position: THREE.Vector2 | THREE.Vector3,
target: THREE.Vector3,
flipY: boolean = false
) {
const worldPos = tile2world(extents, decodeInfo, position, flipY, tempWorldPos);
target.set(worldPos.x, worldPos.y, 0);
tile2world(extents, decodeInfo, position, flipY, target);
decodeInfo.targetProjection.reprojectPoint(webMercatorProjection, target, target);
}

export function webMercatorTile2TargetTile(
extents: number,
decodeInfo: DecodeInfo,
position: THREE.Vector2,
position: THREE.Vector2 | THREE.Vector3,
target: THREE.Vector3,
flipY: boolean = false
) {
Expand Down
16 changes: 8 additions & 8 deletions @here/harp-vectortile-datasource/lib/VectorTileDataEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,14 @@ export class VectorTileDataEmitter {
*
* @param layer - Tile's layer to be processed.
* @param extents - Tile's layer extents.
* @param geometry - The current feature containing the main geometry.
* @param geometry - The feature geometry in local webMercator coordinates.
* @param env - The [[MapEnv]] containing the environment information for the map.
* @param techniques - The array of [[Technique]] that will be applied to the geometry.
*/
processPointFeature(
layer: string,
extents: number,
geometry: THREE.Vector2[],
geometry: THREE.Vector3[],
context: AttrEvaluationContext,
techniques: IndexedTechnique[]
): void {
Expand Down Expand Up @@ -1160,10 +1160,10 @@ export class VectorTileDataEmitter {

case TextureCoordinateType.EquirectangularSpace:
return (tilePos: THREE.Vector2, extents: number): THREE.Vector2 => {
const worldPos = tile2world(extents, this.m_decodeInfo, tilePos, false, tmpV2r);
const worldPos = tile2world(extents, this.m_decodeInfo, tilePos, false, tmpV3r);
const uv = normalizedEquirectangularProjection.reprojectPoint(
webMercatorProjection,
new THREE.Vector3(worldPos.x, worldPos.y, 0)
worldPos
);
return new THREE.Vector2(uv.x, uv.y);
};
Expand All @@ -1173,8 +1173,8 @@ export class VectorTileDataEmitter {
return undefined;
}
return (tilePos: THREE.Vector2, extents: number): THREE.Vector2 => {
const uv = new THREE.Vector2();
tile2world(extents, this.m_decodeInfo, tilePos, false, uv);
const worldPos = tile2world(extents, this.m_decodeInfo, tilePos, false, tmpV3r);
const uv = new THREE.Vector2(worldPos.x, worldPos.y);
if (objectBounds) {
uv.x -= objectBounds.min.x;
uv.y -= objectBounds.min.y;
Expand Down Expand Up @@ -1446,7 +1446,7 @@ export class VectorTileDataEmitter {
this.m_decodeInfo,
tmpV2.set(vertices[i], vertices[i + 1]),
true,
tmpV2r
tmpV3r
);
positionArray.push(worldPos.x, worldPos.y, 0);
if (texCoordType !== undefined) {
Expand Down Expand Up @@ -1508,7 +1508,7 @@ export class VectorTileDataEmitter {
const tilePos = world2tile(
extents,
this.m_decodeInfo,
tmpV2.set(posAttr.array[i], posAttr.array[i + 1]),
tmpV3.set(posAttr.array[i], posAttr.array[i + 1], 0),
true,
tmpV2r
);
Expand Down
5 changes: 4 additions & 1 deletion @here/harp-vectortile-datasource/lib/VectorTileDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,11 @@ export class VectorTileDataProcessor implements IGeometryProcessor {
return this.m_decodedTileEmitter.getDecodedTile();
}

/** @override */
processPointFeature(
layer: string,
extents: number,
geometry: THREE.Vector2[],
geometry: THREE.Vector3[],
env: MapEnv,
storageLevel: number
): void {
Expand Down Expand Up @@ -178,6 +179,7 @@ export class VectorTileDataProcessor implements IGeometryProcessor {
}
}

/** @override */
processLineFeature(
layer: string,
extents: number,
Expand Down Expand Up @@ -229,6 +231,7 @@ export class VectorTileDataProcessor implements IGeometryProcessor {
}
}

/** @override */
processPolygonFeature(
layer: string,
extents: number,
Expand Down

0 comments on commit 2183218

Please sign in to comment.