user10496245
user10496245

Reputation: 217

Check coordinates/places inside polygons in openlayers

I have some code by which I am checking places or coordinates inside in polygons or not. The problem is it may have 10000 places or more which is getting performance issue and map is getting slow. Please find my code below:

    places.forEach(p => {
       this.isInsidePolygons(p.latitude, p.longitude)
    })

    isInsidePolygons(latitude: number, longitude: number): boolean {
                let isInsidePolygon = false;

                var coordinate = OlHelper.transformToEPSG3857([Number(longitude), Number(latitude)]);
                var shapes = this.getShapes();
                for (let i = 0; i < shapes.length; i++) {
                    let features = shapes[i].getSource().getFeatures();
                    if (!features || features.length == 0) continue;

                    for (let j = 0; j < features.length; j++) {
                        var geometry = features[j].getGeometry();
                        isInsidePolygon = geometry.intersectsCoordinate(coordinate);
                        if (isInsidePolygon) break;
                    }
                    if (isInsidePolygon) break;
                }

                return isInsidePolygon;
            }

   getShapes(): ol.layer.Vector[] {
        var shapes = [];
        this.MapControl.getLayers().forEach((layer) => {
            if (layer instanceof ol.layer.Vector) shapes.push(layer);
        });
        return shapes;
    }

Is it possible to check all places are inside polygons in openlayers in a single check instead of looping for each one?

Upvotes: 1

Views: 2177

Answers (2)

SMattia
SMattia

Reputation: 49

You can use turf.js, specifically the function pointsWithinPolygon. Starting from a multi-polygon or a polygon and a list of points the functions it returns all the points inside the polygon, but the complexity is linear with the number of the points. If we use turf.js you have to convert the features in GeoJSON.

let format = new ol.format.GeoJSON();
let pointsInsidePolygon = turf.pointsWithinPolygon(
         format.writeFeatureObject(points),
         format.writeFeatureObject(polygon)
    );

And then to read the GeoJson features

let features =  format.readFeatures(pointsInsidePolygon);

But if you want only now if there is some points inside or outside the polygon, but without knowing which one you can play with the extent of the polygon

Upvotes: 1

Mike
Mike

Reputation: 17872

    let features = shapes[i].getSource().getFeatures();
    if (!features || features.length == 0) continue;

    for (let j = 0; j < features.length; j++) {
        var geometry = features[j].getGeometry();
        isInsidePolygon = geometry.intersectsCoordinate(coordinate);
        if (isInsidePolygon) break;
    }

could be replaced by

    isInsidePolygon = (shapes[i].getSource().getFeaturesAtCoordinate(coordinate).length > 0);

which is less code but probably not much more efficient

To test shapes against the more numerous places you need need to create a vector source for your places. Use the extent of the shapes to get a shortlist of places, and only test those against the shape geometry

    let placesFeatures = [];
    places.forEach((p) => {
        placesFeatures.push(new Feature({
            geometry: new Point(fromLonLat[p.longitude, p.latitude]))
            id: [p.latitude, p.longitude].toString,
            isInsidePolygon: false
        }))
    })
    let placesSource = new VectorSource({features: placesFeatures});

    for (let i = 0; i < shapes.length; i++) {
        let features = shapes[i].getSource().getFeatures();
        if (!features || features.length == 0) continue;

        for (let j = 0; j < features.length; j++) {
            var geometry = features[j].getGeometry();
            let extent = features[j].getGeometry().getExtent();
            let candidates = placesSource.getFeaturesInExtent(geometry.getExtent());
            if (!candidates || candidates.length == 0) continue;

            for (let k = 0; k < candidates.length; k++) {
                isInsidePolygon = geometry.intersectsCoordinate(candidates[k].getGeometry().getCoordinates());
                if (isInsidePolygon) {
                    candidates[k].set('isInsidePolygon', true);
                }
            })
        }
    }

Then you can get results from the places features in the source

    isInsidePolygons(latitude: number, longitude: number): boolean {
        return placesSource.getFeatureById([latitude, longitude].toString()).get('isInsidePolygon');
    }

Upvotes: 3

Related Questions