2

I have a square polygon and I want to cut circles into it where I click with the mouse. The result should be a new polygon with holes in it.

This is my attempt so far but I can't get it to work. The idea is to create a "fog of war" effect where the map gets revealed at the position of the user.

<!DOCTYPE html>
Fog of War using OpenLayers
<script>
    const REVEAL_RADIUS = 50; 
    const COORDS = ol.proj.fromLonLat([13, 52]);

    const map = new ol.Map({
        target: 'map',
        layers: [
            new ol.layer.Tile({
                source: new ol.source.OSM()
            })
        ],
        view: new ol.View({
            center: COORDS,
            zoom: 14
        })
    });

    const fogSource = new ol.source.Vector();

    const fogLayer = new ol.layer.Vector({
        source: fogSource,
        style: new ol.style.Style({
            fill: new ol.style.Fill({
                color: 'rgba(0, 0, 0, 0.7)'
            })
        })
    });

    map.addLayer(fogLayer);

    const worldExtent = [-180, -90, 180, 90];
    const fogFeature = new ol.Feature({
        geometry: ol.geom.Polygon.fromExtent(ol.proj.transformExtent(worldExtent, 'EPSG:4326', 'EPSG:3857'))
    });
    fogSource.addFeature(fogFeature);

    map.on('click', function(e) {
        const resolution = map.getView().getResolution();
        const adjustedRadius = REVEAL_RADIUS * resolution;

        const circleGeom = new ol.geom.Circle(e.coordinate, adjustedRadius);
        const polygonGeom = ol.geom.Polygon.fromCircle(circleGeom);

        const jstsParser = new jsts.io.OL3Parser();
        jstsParser.inject(ol.geom.Point, ol.geom.LineString, ol.geom.LinearRing, ol.geom.Polygon, ol.geom.MultiPolygon);

        const fogJsts = jstsParser.read(fogFeature.getGeometry());
        const circleJsts = jstsParser.read(polygonGeom);

        const subtractedJsts = fogJsts.difference(circleJsts);
        const subtractedGeom = jstsParser.write(subtractedJsts);

        fogFeature.setGeometry(subtractedGeom);
        fogSource.refresh();
    });
</script>

1 Answer 1

5
map.on('click', function(e) {
    const resolution = map.getView().getResolution();
    const adjustedRadius = REVEAL_RADIUS * resolution;
    const circleGeom = new ol.geom.Circle(e.coordinate, adjustedRadius);
    const polygonGeom = ol.geom.Polygon.fromCircle(circleGeom);
    fogFeature.getGeometry().appendLinearRing(polygonGeom.getLinearRing(0));
});

will add a circular hole to the polygon - it will not check if the hole overlaps an existing hole.

UPDATE

You could handle overlapping holes using turf.intersect https://turfjs.org/docs/#intersect

const initalFeature = fogFeature.clone();

map.on('click', function(e) {
    const resolution = map.getView().getResolution();
    const adjustedRadius = REVEAL_RADIUS * resolution;
    const circleGeom = new ol.geom.Circle(e.coordinate, adjustedRadius);
    const polygonGeom = ol.geom.Polygon.fromCircle(circleGeom);
    const tempFeature = initalFeature.clone();
    tempFeature.getGeometry().appendLinearRing(polygonGeom.getLinearRing(0));
    const turfFeature1 = new ol.format.GeoJSON().writeFeatureObject(fogFeature, {rightHanded: true});
    const turfFeature2 = new ol.format.GeoJSON().writeFeatureObject(tempFeature, {rightHanded: true});
    const subtractedGeom = new ol.format.GeoJSON().readFeature(turf.intersect(turfFeature1, turfFeature2)).getGeometry();
    fogFeature.setGeometry(subtractedGeom);
});

Working example https://jsbin.com/kusumopage/edit?html,output

Note that if you have a filled area surrounded by holes the geometry will be a MultiPolygon

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.