Skip to content

Commit

Permalink
feat: add the addFeatures method to allow adding of external data
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesLMilner committed Jun 4, 2023
1 parent 157c1b3 commit b6e0043
Show file tree
Hide file tree
Showing 31 changed files with 1,314 additions and 306 deletions.
14 changes: 0 additions & 14 deletions development/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,13 @@ const example = {
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);

// const dataString = localStorage.getItem("snapshot");

// console.log(dataString, Boolean(dataString));
// const data = Boolean(dataString) ? JSON.parse(dataString) : undefined;

const draw = new TerraDraw({
adapter: new TerraDrawLeafletAdapter({
lib: L,
map,
coordinatePrecision: 9,
}),
modes: getModes(),
// data,
});

draw.start();
Expand Down Expand Up @@ -214,10 +208,6 @@ const example = {
coordinatePrecision: 9,
}),
modes: getModes(),
// data: uk.features.map((feature) => {
// feature.properties = feature.properties || {};
// (feature.properties as any).mode = "arbitary";
// }) as any,
});

draw.start();
Expand Down Expand Up @@ -253,10 +243,6 @@ const example = {
coordinatePrecision: 9,
}),
modes: getModes(),
// data: uk.features.map((feature) => {
// feature.properties = feature.properties || {};
// (feature.properties as any).mode = "arbitary";
// }) as any,
});

draw.start();
Expand Down
41 changes: 41 additions & 0 deletions guides/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,47 @@ Coming soon - please see development folder for example for now.

Coming soon - please see development folder for example for now.

## Common Patterns

### Loading in External Data

It is common pattern to want to load in data from an external source (GeoJSON file, API call, etc). This can be achieved with the `addFeatures` method on the Terra Draw instance. The method call works out which mode to add the feature based on looking at its `mode` property in the Features `properties` property. All modes have a method called `validateFeature` that ensures that a given feature is valid for the mode. For example if you wanted to add a series of points to the TerraDrawPointMode you could do this by ensuring that the points you feed in have the `mode` property set to `point`.

```javascript
points.forEach((point) => {
point.properties.mode = "point";
});

draw.addFeatures(points);
```

### Render Only Modes with TerraDrawRenderMode

If you just want to render some data onto the map without it being editable, you can use `TerraDrawRenderMode` in combination with `addFeatures` like so:

```javascript
const draw = new TerraDraw({
adapter: new TerraDrawLeafletAdapter({
lib: L,
map,
coordinatePrecision: 9,
}),
modes: {
arbitary: new TerraDrawRenderMode(),
},
});

draw.start();

points.forEach((point) => {
point.properties.mode = "arbitary";
});

draw.addFeatures(points);

// This will add the points to hte TerraDrawRenderMode 'arbitary' rendering them to the screen
```

## Other Examples

There are a few other working examples that you can use as points of reference for creating a new app using Terra Draw.
Expand Down
86 changes: 86 additions & 0 deletions src/geometry/boolean/is-valid-coordinate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
validLatitude,
validLongitude,
coordinateIsValid,
getDecimalPlaces,
} from "./is-valid-coordinate";

describe("validLatitude", () => {
it("should return true for valid latitude", () => {
expect(validLatitude(45)).toBe(true);
});

it("should return false for latitude greater than 90", () => {
expect(validLatitude(91)).toBe(false);
});

it("should return false for latitude less than -90", () => {
expect(validLatitude(-91)).toBe(false);
});

it("should return true for boundary values -90 and 90", () => {
expect(validLatitude(-90)).toBe(true);
expect(validLatitude(90)).toBe(true);
});

it("should return true for valid longitude", () => {
expect(validLongitude(90)).toBe(true);
});

it("should return false for longitude greater than 180", () => {
expect(validLongitude(181)).toBe(false);
});

it("should return false for longitude less than -180", () => {
expect(validLongitude(-181)).toBe(false);
});

it("should return true for boundary values -180 and 180", () => {
expect(validLongitude(-180)).toBe(true);
expect(validLongitude(180)).toBe(true);
});
});

describe("coordinateIsValid", () => {
it("should return true for valid coordinate", () => {
expect(coordinateIsValid([45, 90], 9)).toBe(true);
});

it("should return false for coordinate with incorrect length", () => {
expect(coordinateIsValid([45], 9)).toBe(false);
expect(coordinateIsValid([45, 90, 100], 9)).toBe(false);
});

it("should return false for coordinate with non-number elements", () => {
expect(coordinateIsValid(["45", 90], 9)).toBe(false);
expect(coordinateIsValid([45, "90"], 9)).toBe(false);
expect(coordinateIsValid(["45", "90"], 9)).toBe(false);
});

it("should return false for coordinate with invalid longitude and latitude", () => {
expect(coordinateIsValid([181, 90], 9)).toBe(false);
expect(coordinateIsValid([45, 91], 9)).toBe(false);
expect(coordinateIsValid([-181, -91], 9)).toBe(false);
});
});

describe("getDecimalPlaces", () => {
it("returns 0 for an integer", () => {
expect(getDecimalPlaces(10)).toBe(0);
expect(getDecimalPlaces(0)).toBe(0);
});

it("returns the correct number of decimal places for a float", () => {
expect(getDecimalPlaces(0.1)).toBe(1);
expect(getDecimalPlaces(0.01)).toBe(2);
expect(getDecimalPlaces(0.123456)).toBe(6);
});

it("returns the correct number of decimal places for a float greater than 1", () => {
expect(getDecimalPlaces(1.23)).toBe(2);
});

it("returns the correct number of decimal places for a float less than 0", () => {
expect(getDecimalPlaces(-0.123)).toBe(3);
});
});
35 changes: 35 additions & 0 deletions src/geometry/boolean/is-valid-coordinate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export function validLatitude(lat: number) {
return lat >= -90 && lat <= 90;
}

export function validLongitude(lng: number) {
return lng >= -180 && lng <= 180;
}

export function coordinateIsValid(
coordinate: unknown[],
coordinatePrecision: number
) {
return (
coordinate.length === 2 &&
typeof coordinate[0] === "number" &&
typeof coordinate[1] === "number" &&
coordinate[0] !== Infinity &&
coordinate[1] !== Infinity &&
validLongitude(coordinate[0]) &&
validLatitude(coordinate[1]) &&
getDecimalPlaces(coordinate[0]) <= coordinatePrecision &&
getDecimalPlaces(coordinate[1]) <= coordinatePrecision
);
}

export function getDecimalPlaces(value: number): number {
let current = 1;
let precision = 0;
while (Math.round(value * current) / current !== value) {
current *= 10;
precision++;
}

return precision;
}
62 changes: 62 additions & 0 deletions src/geometry/boolean/is-valid-linestring-feature.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Feature, LineString } from "geojson";
import { isValidLineStringFeature } from "./is-valid-linestring-feature";

describe("isValidLineStringFeature", () => {
it("returns true for a valid LineString feature with correct coordinate precision", () => {
const validFeature = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[45, 80],
[46, 81],
],
},
} as Feature<LineString, Record<string, any>>;
expect(isValidLineStringFeature(validFeature, 9)).toBe(true);
});

it("returns false for a non-LineString feature", () => {
const nonLineStringFeature = {
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[45, 80],
[46, 81],
[45, 80],
],
},
} as any;
expect(isValidLineStringFeature(nonLineStringFeature, 9)).toBe(false);
});

it("returns false for a LineString feature with less than 2 coordinates", () => {
const lessCoordinatesFeature = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [[45, 90]],
},
} as Feature<LineString, Record<string, any>>;
expect(isValidLineStringFeature(lessCoordinatesFeature, 9)).toBe(false);
});

it("returns false for a LineString feature with incorrect coordinate precision", () => {
const validFeature = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[45.123, 80.123],
[46.123, 81.123],
],
},
} as Feature<LineString, Record<string, any>>;
expect(isValidLineStringFeature(validFeature, 2)).toBe(false);
});
});
15 changes: 15 additions & 0 deletions src/geometry/boolean/is-valid-linestring-feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { GeoJSONStoreFeatures } from "../../terra-draw";
import { coordinateIsValid } from "./is-valid-coordinate";

export function isValidLineStringFeature(
feature: GeoJSONStoreFeatures,
coordinatePrecision: number
): boolean {
return (
feature.geometry.type === "LineString" &&
feature.geometry.coordinates.length >= 2 &&
feature.geometry.coordinates.every((coordinate) =>
coordinateIsValid(coordinate, coordinatePrecision)
)
);
}
43 changes: 43 additions & 0 deletions src/geometry/boolean/is-valid-point.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Feature, Point } from "geojson";
import { isValidPoint } from "./is-valid-point";

describe("isValidPoint", () => {
it("returns true for a valid Point with correct coordinate precision", () => {
const validPoint = {
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: [45, 90],
},
} as Feature<Point, Record<string, any>>;
expect(isValidPoint(validPoint, 2)).toBe(true);
});

it("returns false for a non-Point feature", () => {
const nonPointFeature = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[45, 90],
[46, 91],
],
},
} as any;
expect(isValidPoint(nonPointFeature, 2)).toBe(false);
});

it("returns false for a Point with incorrect coordinate precision", () => {
const invalidPoint = {
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: [45.123, 90.123],
},
} as Feature<Point, Record<string, any>>;
expect(isValidPoint(invalidPoint, 2)).toBe(false);
});
});
12 changes: 12 additions & 0 deletions src/geometry/boolean/is-valid-point.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { GeoJSONStoreFeatures } from "../../terra-draw";
import { coordinateIsValid } from "./is-valid-coordinate";

export function isValidPoint(
feature: GeoJSONStoreFeatures,
coordinatePrecision: number
): boolean {
return (
feature.geometry.type === "Point" &&
coordinateIsValid(feature.geometry.coordinates, coordinatePrecision)
);
}
Loading

0 comments on commit b6e0043

Please sign in to comment.