forked from MapComplete/MapComplete
Add clipping to generateCache
This commit is contained in:
parent
f7f0ccdb7d
commit
509b237d02
3 changed files with 177 additions and 90 deletions
|
@ -15,6 +15,11 @@ import togpx from "togpx"
|
|||
import Constants from "../Models/Constants"
|
||||
|
||||
export class GeoOperations {
|
||||
/**
|
||||
* Create a union between two features
|
||||
*/
|
||||
static union = turf.union
|
||||
static intersect = turf.intersect
|
||||
private static readonly _earthRadius = 6378137
|
||||
private static readonly _originShift = (2 * Math.PI * GeoOperations._earthRadius) / 2
|
||||
|
||||
|
@ -158,35 +163,6 @@ export class GeoOperations {
|
|||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which does the heavy lifting for 'inside'
|
||||
*/
|
||||
private static pointInPolygonCoordinates(
|
||||
x: number,
|
||||
y: number,
|
||||
coordinates: [number, number][][]
|
||||
) {
|
||||
const inside = GeoOperations.pointWithinRing(
|
||||
x,
|
||||
y,
|
||||
/*This is the outer ring of the polygon */ coordinates[0]
|
||||
)
|
||||
if (!inside) {
|
||||
return false
|
||||
}
|
||||
for (let i = 1; i < coordinates.length; i++) {
|
||||
const inHole = GeoOperations.pointWithinRing(
|
||||
x,
|
||||
y,
|
||||
coordinates[i] /* These are inner rings, aka holes*/
|
||||
)
|
||||
if (inHole) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect wether or not the given point is located in the feature
|
||||
*
|
||||
|
@ -620,6 +596,113 @@ export class GeoOperations {
|
|||
return copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes two points and finds the geographic bearing between them, i.e. the angle measured in degrees from the north line (0 degrees)
|
||||
*/
|
||||
public static bearing(a: Coord, b: Coord): number {
|
||||
return turf.bearing(a, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if one feature contains the other feature
|
||||
*
|
||||
* const pond: Feature<Polygon, any> = {
|
||||
* "type": "Feature",
|
||||
* "properties": {"natural":"water","water":"pond"},
|
||||
* "geometry": {
|
||||
* "type": "Polygon",
|
||||
* "coordinates": [[
|
||||
* [4.362924098968506,50.8435422298544 ],
|
||||
* [4.363272786140442,50.8435219059949 ],
|
||||
* [4.363213777542114,50.8437420806679 ],
|
||||
* [4.362924098968506,50.8435422298544 ]
|
||||
* ]]}}
|
||||
* const park: Feature<Polygon, any> = {
|
||||
* "type": "Feature",
|
||||
* "properties": {"leisure":"park"},
|
||||
* "geometry": {
|
||||
* "type": "Polygon",
|
||||
* "coordinates": [[
|
||||
* [ 4.36073541641235,50.84323737103244 ],
|
||||
* [ 4.36469435691833, 50.8423905305197 ],
|
||||
* [ 4.36659336090087, 50.8458997374786 ],
|
||||
* [ 4.36254858970642, 50.8468007074916 ],
|
||||
* [ 4.36073541641235, 50.8432373710324 ]
|
||||
* ]]}}
|
||||
* GeoOperations.completelyWithin(pond, park) // => true
|
||||
* GeoOperations.completelyWithin(park, pond) // => false
|
||||
*/
|
||||
static completelyWithin(
|
||||
feature: Feature<Geometry, any>,
|
||||
possiblyEncloingFeature: Feature<Polygon | MultiPolygon, any>
|
||||
): boolean {
|
||||
return booleanWithin(feature, possiblyEncloingFeature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an intersection between two features.
|
||||
* A new feature is returned based on 'toSplit', which'll have a geometry that is completely withing boundary
|
||||
*/
|
||||
public static clipWith(toSplit: Feature, boundary: Feature<Polygon>): Feature[] {
|
||||
if (toSplit.geometry.type === "Point") {
|
||||
const p = <Feature<Point>>toSplit
|
||||
if (GeoOperations.inside(p.geometry.coordinates, boundary)) {
|
||||
return [p]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
if (toSplit.geometry.type === "LineString") {
|
||||
const splitup = turf.lineSplit(<Feature<LineString>>toSplit, boundary)
|
||||
const kept = []
|
||||
for (const f of splitup.features) {
|
||||
const ls = <Feature<LineString>>f
|
||||
if (!GeoOperations.inside(GeoOperations.centerpointCoordinates(f), boundary)) {
|
||||
continue
|
||||
}
|
||||
f.properties = { ...toSplit.properties }
|
||||
kept.push(f)
|
||||
}
|
||||
return kept
|
||||
}
|
||||
if (toSplit.geometry.type === "Polygon" || toSplit.geometry.type == "MultiPolygon") {
|
||||
const splitup = turf.intersect(<Feature<Polygon>>toSplit, boundary)
|
||||
splitup.properties = { ...toSplit.properties }
|
||||
return [splitup]
|
||||
}
|
||||
throw "Invalid geometry type with GeoOperations.clipWith: " + toSplit.geometry.type
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which does the heavy lifting for 'inside'
|
||||
*/
|
||||
private static pointInPolygonCoordinates(
|
||||
x: number,
|
||||
y: number,
|
||||
coordinates: [number, number][][]
|
||||
) {
|
||||
const inside = GeoOperations.pointWithinRing(
|
||||
x,
|
||||
y,
|
||||
/*This is the outer ring of the polygon */ coordinates[0]
|
||||
)
|
||||
if (!inside) {
|
||||
return false
|
||||
}
|
||||
for (let i = 1; i < coordinates.length; i++) {
|
||||
const inHole = GeoOperations.pointWithinRing(
|
||||
x,
|
||||
y,
|
||||
coordinates[i] /* These are inner rings, aka holes*/
|
||||
)
|
||||
if (inHole) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private static pointWithinRing(x: number, y: number, ring: [number, number][]) {
|
||||
let inside = false
|
||||
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
||||
|
@ -740,57 +823,4 @@ export class GeoOperations {
|
|||
}
|
||||
throw "CalculateIntersection fallthrough: can not calculate an intersection between features"
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes two points and finds the geographic bearing between them, i.e. the angle measured in degrees from the north line (0 degrees)
|
||||
*/
|
||||
public static bearing(a: Coord, b: Coord): number {
|
||||
return turf.bearing(a, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if one feature contains the other feature
|
||||
*
|
||||
* const pond: Feature<Polygon, any> = {
|
||||
* "type": "Feature",
|
||||
* "properties": {"natural":"water","water":"pond"},
|
||||
* "geometry": {
|
||||
* "type": "Polygon",
|
||||
* "coordinates": [[
|
||||
* [4.362924098968506,50.8435422298544 ],
|
||||
* [4.363272786140442,50.8435219059949 ],
|
||||
* [4.363213777542114,50.8437420806679 ],
|
||||
* [4.362924098968506,50.8435422298544 ]
|
||||
* ]]}}
|
||||
* const park: Feature<Polygon, any> = {
|
||||
* "type": "Feature",
|
||||
* "properties": {"leisure":"park"},
|
||||
* "geometry": {
|
||||
* "type": "Polygon",
|
||||
* "coordinates": [[
|
||||
* [ 4.36073541641235,50.84323737103244 ],
|
||||
* [ 4.36469435691833, 50.8423905305197 ],
|
||||
* [ 4.36659336090087, 50.8458997374786 ],
|
||||
* [ 4.36254858970642, 50.8468007074916 ],
|
||||
* [ 4.36073541641235, 50.8432373710324 ]
|
||||
* ]]}}
|
||||
* GeoOperations.completelyWithin(pond, park) // => true
|
||||
* GeoOperations.completelyWithin(park, pond) // => false
|
||||
*/
|
||||
static completelyWithin(
|
||||
feature: Feature<Geometry, any>,
|
||||
possiblyEncloingFeature: Feature<Polygon | MultiPolygon, any>
|
||||
): boolean {
|
||||
return booleanWithin(feature, possiblyEncloingFeature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a union between two features
|
||||
*/
|
||||
static union = turf.union
|
||||
|
||||
/**
|
||||
* Create an intersection between two features
|
||||
*/
|
||||
static intersect = turf.intersect
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue