2

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

luzhskij
  • 31
  • 6
  • 3
    A style's geometry must be a single geometry, not an array, and linear ring is not a styleable geometry. But you could convert the polygon's rings to a styleable MultiLineString `lineStyle.setGeometry(new ol.geom.MultiLineString(geom.getCoordinates()));` – Mike Dec 21 '21 at 14:16
  • 2
    https://codepen.io/mike-000/pen/qBPXpVq – Mike Dec 21 '21 at 14:29
  • @Mike! Thanks, it really works! The question remains, is it possible to get a polygon with a hole in a more beautiful way. – luzhskij Dec 21 '21 at 14:29
  • If you are simply wanting a multi-color outer stroke you could style that as in https://stackoverflow.com/questions/57421223/openlayers-3-offset-stroke-style – Mike Dec 21 '21 at 14:58
  • Hi, @Mike! Yes, I have seen this example, very interesting. I tried to implement a similar mechanism in myself, but somehow it was unsuccessful. At the moment, the question is: 1) how to count "dist" so that the inner and outer strokes are without gaps 2) when calculating the inner stroke, loops are obtained. This can be clearly seen with a small stroke thickness. And when the scale changes, then it comes out completely horror. ----- added pictures to the question and updated the example: [codepen link](https://codepen.io/luzhskij/pen/VwMMYJr) – luzhskij Dec 22 '21 at 13:29

1 Answers1

2

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.

Mike
  • 16,042
  • 2
  • 14
  • 30
  • On a scale of 14-20, it looks like it should. But if you take 0-13, then it's terrible. Also, the display depends not only on the scale, but also on the size of the original polygon. Due to the fact that my inner line (inner buffer) should be thicker (about 3 times relative to the outer stroke), then at these scales it spreads out in all directions (outside the parent polygon). I've updated the example with your code. Thanks for participating! [codepen example](https://codepen.io/luzhskij/pen/VwMMYJr) – luzhskij Dec 23 '21 at 14:02
  • I think there must be some relationship between the size of the inner stroke at a certain scale and the size of the parent polygon. The moment the stroke starts to go out of bounds, just completely fill the polygon with the inner stroke color. – luzhskij Dec 23 '21 at 14:21
  • You could limit the inner width based on the resolution, e.g. `var widths = [12 / Math.max(1, resolution / 10), 4];` – Mike Dec 23 '21 at 14:32
  • https://codepen.io/mike-000/pen/ZEXXZOV – Mike Dec 23 '21 at 22:57
  • thank you! This seems to be true. I tried to go through the area comparison, but there are some nuances. – luzhskij Dec 24 '21 at 05:59
  • [codepen new example](https://codepen.io/luzhskij/pen/vYeWBWr) But, unfortunately, it also works incorrectly. – luzhskij Dec 24 '21 at 12:47
  • The resolution depends on the projection units. I degree in EPSG:4326 is about 220000 meters in EPSG:3857 Also your polygons are smaller so you will need something like `var widths = [12 / Math.max(1, resolution * 400000), 4];` – Mike Dec 24 '21 at 13:20
  • In the previous example, such a projection is only to show the faster%) Still, it is more accurate to show an example at 3857. --- But there is a mistake. The display is incorrect. The inner buffer is outside the parent polygon. --- https://codepen.io/luzhskij/pen/gOGXaEr – luzhskij Dec 24 '21 at 13:39
  • Yes, the polygons are much smaller than your first 3857 example so instead of `resolution / 10` you need about `resolution * 2`, or in 4326 `resolution * 2 * 220000` It would be better to limit the width based on the polygon's dimensions, for example 0.25 of the smallest side of its extent https://codepen.io/mike-000/pen/JjrOGRP which should work regardless of the projection because resolution and size will be in the same units. – Mike Dec 24 '21 at 14:17
  • Oh yes, I have polygons of various sizes and configurations. This option really works better! There is still something that looks like thorns on some objects. In such places, the inner stroke crawls out. In the example, such objects are also visible (the largest polygon, approximately a scale of 0-17). In general, it is very strange that openlayers or other libraries do not have built-in functionality for building an internal buffer. – luzhskij Dec 24 '21 at 14:38