OpenLayers creating a complex style (polygon with a hole and a stroke on one side)

981 Views Asked by At

There was a difficulty in creating a complex polygon style. The wording is as follows:

  • the polygon should be drawn as a polygon with a hole and a stroke on the outside.

In a difficult (as it seems to me) way, I made drawing a polygon with a hole:

  1. convert to turf
  2. using turf.buffer and a negative buffer value, I get an internal buffer
  3. using turf.difference (source polygon and buffer) I get a polygon with a hole

But I don't understand how to draw the border only from the outside%) If in the same function I try to return 2 styles (line + polygon), then I get an error (Uncaught TypeError: s.simplifyTransformed is not a function).

In general, is it possible to return 2 different geometries in the style?

In the picture the red polygon is what I need to get in the end.

In the picture

Also I made a minimal example on codepen

I would be grateful for your help!


upd.

loops enter image description here

and zoom out enter image description here

1

There are 1 best solutions below

10
Mike On

To adapt the OpenLayers 3: Offset stroke style example for a polygon you would need to extend the ring by one segment at each end so you can correctly calculate the new coordinates at the original start/end point, then remove the excess when creating the resulting polygon.

var style = function(feature, resolution) {
  var poly = feature.getGeometry();
  if (poly.getType() == 'Polygon') {
var coordinates = poly.getCoordinates()[0];
coordinates = coordinates.slice(-2, -1).concat(coordinates).concat(coordinates.slice(1, 2));
var geom = new ol.geom.LineString(coordinates);
var colors = ['green', 'yellow', 'red'];
var width = 4;
var styles = [];
for (var line = 0; line < colors.length; line++) {
    var dist = width * resolution * (line - (colors.length-1)/2);
    var coords = [];
    var counter = 0;
    geom.forEachSegment(function(from, to) {
        var angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
        var newFrom = [
            Math.sin(angle) * dist + from[0],
            -Math.cos(angle) * dist + from[1]
        ];
        var newTo = [
            Math.sin(angle) * dist + to[0],
            -Math.cos(angle) * dist + to[1]
        ];
        coords.push(newFrom);
        coords.push(newTo);
        if (coords.length > 2) {
            var intersection = math.intersect(coords[counter], coords[counter+1], coords[counter+2], coords[counter+3]);
            coords[counter+1] = (intersection) ? intersection : coords[counter+1];
            coords[counter+2] = (intersection) ? intersection : coords[counter+2];
            counter += 2;
        }
    });
    styles.push(
        new ol.style.Style({
            geometry: new ol.geom.Polygon([coords.slice(2, -1)]),
            stroke: new ol.style.Stroke({
                color: colors[line],
                width: width
            })
        })
    );
}
return styles;
  }
};


var raster = new ol.layer.Tile({
  source:  new ol.source.OSM() 
});

var source = new ol.source.Vector();

var vector = new ol.layer.Vector({
  source: source,
  style: style
});

var map = new ol.Map({
  layers: [raster, vector],
  target: 'map',
  view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
  })
});

map.addInteraction(new ol.interaction.Draw({
  source: source,
  type: 'Polygon',
  style: style
}));
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>

There is a problem with the original algorithm for LineStrings at corners with multiple vertices

enter image description here

When zoomed out the two vertices on the inner line should merge to a single point, but that is not happening, instead they cross and cause a kink in the line.