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

Commit

Permalink
HARP-13254: Enfore the winding convention expected by the vt decoder.
Browse files Browse the repository at this point in the history
This change ensures that the winding of the polygons decoded by
the tiled GeoJson adapter follow the convention expected by the
rendering techniques.

Signed-off-by: Roberto Raggi <roberto.raggi@here.com>
  • Loading branch information
robertoraggi committed Feb 2, 2021
1 parent 4e82235 commit 34bfbb4
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,11 @@ function signedPolygonArea(contour: GeoPointLike[]): number {
}

function convertRings(coordinates: GeoPointLike[][], decodeInfo: DecodeInfo): IPolygonGeometry {
let outerWinding: boolean | undefined;
const rings = coordinates.map((ring, i) => {
const isOuterRing = i === 0;
const isClockWise = signedPolygonArea(ring) < 0;
const { positions } = convertLineStringGeometry(ring, decodeInfo);
const winding = signedPolygonArea(ring) < 0;
if (i === 0) {
outerWinding = winding;
} else if (winding === outerWinding) {
if ((isOuterRing && !isClockWise) || (!isOuterRing && isClockWise)) {
positions.reverse();
}
return positions;
Expand Down
178 changes: 138 additions & 40 deletions test/rendering/GeoJsonFeaturesRendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@

import {
FeatureCollection,
FlatTheme,
LineCaps,
SolidLineTechniqueParams,
Style
} from "@here/harp-datasource-protocol";
import { GeoPointLike } from "@here/harp-geoutils";
import { LookAtParams } from "@here/harp-mapview";
import * as turf from "@turf/turf";
import { assert } from "chai";
import * as THREE from "three";

import { GeoJsonStore } from "./utils/GeoJsonStore";
import { GeoJsonTest } from "./utils/GeoJsonTest";
Expand Down Expand Up @@ -158,57 +162,151 @@ describe("GeoJson features", function () {
});

describe("extruded-polygon technique", async function () {
it("Polygon touching tile border", async function () {
const polygon: turf.Feature = {
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[
[0.00671649362467299, 51.49353450058163, 0],
[0.006598810705050831, 51.48737650885326, 0],
[0.015169103358567556, 51.48731300784492, 0],
[0.015286786278189713, 51.49347100814989, 0],
[0.006716493624672999, 51.49353450058163, 0],
[0.00671649362467299, 51.49353450058163, 0]
]
]
// Helper function to test the winding of the rings.

const isClockWise = (ring: turf.Position[]) =>
THREE.ShapeUtils.isClockWise(ring.map(p => new THREE.Vector2().fromArray(p)));

// the theme used by this test suite.
const theme: FlatTheme = {
lights,
styles: [
{
styleSet: "geojson",
technique: "extruded-polygon",
color: "#0335a2",
lineWidth: 1,
lineColor: "red",
constantHeight: true,
height: 300
}
};
]
};

// the polygon feature
const polygon = turf.polygon([
[
[0.00671649362467299, 51.49353450058163, 0],
[0.006598810705050831, 51.48737650885326, 0],
[0.015169103358567556, 51.48731300784492, 0],
[0.015286786278189713, 51.49347100814989, 0],
[0.006716493624672999, 51.49353450058163, 0],
[0.00671649362467299, 51.49353450058163, 0]
]
]);

// the center geo location of the polygon
const [longitude, latitude] = turf.center(polygon).geometry!.coordinates;

// the default camera setup used by this test suite.
const lookAt: Partial<LookAtParams> = {
target: [longitude, latitude],
zoomLevel: 15,
tilt: 40,
heading: 45
};

it("Clockwise polygon touching tile border", async function () {
const dataProvider = new GeoJsonStore();

dataProvider.features.insert(polygon);

const geoJson = turf.featureCollection([polygon]);
await geoJsonTest.run({
mochaTest: this,
dataProvider,
testImageName: `geojson-extruded-polygon-touching-tile-bounds`,
lookAt,
theme
});
});

it("Clockwise polygon with a hole", async function () {
const dataProvider = new GeoJsonStore();

const innerPolygon = turf.transformScale(polygon, 0.8, { origin: "center" });

const [longitude, latitude] = turf.center(geoJson).geometry!.coordinates;
const polygonWithHole = turf.difference(polygon, innerPolygon)!;

dataProvider.features.insert(polygonWithHole);

assert.strictEqual(polygonWithHole?.geometry?.coordinates.length, 2);

const outerRing = polygonWithHole.geometry!.coordinates[0] as turf.Position[];
const innerRing = polygonWithHole.geometry!.coordinates[1] as turf.Position[];

// test that the winding of the inner ring is opposite to the winding
// of the outer ring.
assert.notStrictEqual(isClockWise(outerRing), isClockWise(innerRing));

await geoJsonTest.run({
mochaTest: this,
dataProvider,
testImageName: `geojson-extruded-polygon-touching-tile-bounds`,
lookAt: {
target: [longitude, latitude],
zoomLevel: 15,
tilt: 55,
heading: 45
},
theme: {
lights,
styles: [
{
styleSet: "geojson",
technique: "extruded-polygon",
color: "#0335a2",
lineWidth: 1,
lineColor: "red",
constantHeight: true,
height: 300
}
]
}
testImageName: `geojson-extruded-polygon-cw-with-hole`,
lookAt,
theme
});
});

it("Clockwise polygon with a hole wrong winding", async function () {
const dataProvider = new GeoJsonStore();

const innerPolygon = turf.transformScale(polygon, 0.8, { origin: "center" });

const polygonWithHole = turf.difference(polygon, innerPolygon)!;

dataProvider.features.insert(polygonWithHole);

assert.strictEqual(polygonWithHole?.geometry?.coordinates.length, 2);

const outerRing = polygonWithHole.geometry!.coordinates[0] as turf.Position[];
const innerRing = polygonWithHole.geometry!.coordinates[1] as turf.Position[];

// reverse the winding of the inner ring so to have the same winding
// of the outer ring
innerRing.reverse();

// verify that the winding of the rings is the same
assert.strictEqual(isClockWise(outerRing), isClockWise(innerRing));

await geoJsonTest.run({
mochaTest: this,
dataProvider,
testImageName: `geojson-extruded-polygon-cw-wrong-winding-of-inner-ring`,
lookAt,
theme
});
});

it("Clockwise multi-polygon with a hole wrong winding", async function () {
const dataProvider = new GeoJsonStore();

const scaledPolygon = turf.transformScale(polygon, 0.5, { origin: "center" });

const innerPolygon = turf.transformScale(scaledPolygon, 0.8, { origin: "center" });

const polygonWithHole = turf.difference(scaledPolygon, innerPolygon)!;

const otherPolygonWithHole = turf.clone(polygonWithHole)!;

turf.transformTranslate(polygonWithHole, 200, 90, { units: "meters", mutate: true });

turf.transformTranslate(otherPolygonWithHole, 200, -90, {
units: "meters",
mutate: true
});

const multi = turf.union(polygonWithHole as any, otherPolygonWithHole as any);

assert.strictEqual(multi.geometry?.type, "MultiPolygon");

dataProvider.features.insert(multi);

await geoJsonTest.run({
mochaTest: this,
dataProvider,
testImageName: `geojson-extruded-multi-polygon-cw-wrong-winding-of-inner-ring`,
lookAt,
theme
});
});
});
Expand Down

0 comments on commit 34bfbb4

Please sign in to comment.