OpenLayers 3: Offset stroke style

天大地大妈咪最大 提交于 2021-02-08 08:52:28

问题


I have LineString features styled by a single stroke. Some of these features represent two things, so in addition to the initial stroke color, I would like to have a parallel line next to it in a different color. This would be a no-brainer if ol/style/Stroke had an offset property like ol/style/Image does, but sadly, it does not. If I created offset geometries for those features, they wouldn't be visible from a zoomed out level. What are my options?


回答1:


You can offset the geometries based on resolution

var style = function(feature, resolution) {
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 geom = feature.getGeometry();
    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.LineString(coords),
            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: 'LineString'
}));
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>

The intersect test prevents effects like this on sharp angles

For each each segment of the feature geometry the style function calculates parallel segments set to be 4 pixels apart regardless of the resolution (in this case line width and spacing are the same) for the styling geometries centered around the original segment (e.g. if three colors the center style line would follow the original and the outer style lines are offset by +/- 4, if two colors each lines would be offset by +/- 2) Initially the parallel segments are the same length as the original but need to be increased at outside angles or reduced at inside angles so math.intersect is used to find the point where they should join to form a continuous line. Finally for each color the completed linestring is used as the geometry for the color's style, and an array containing each style is returned.

Here is a second example where instead of simple colors and fixed width each offset line has its own width and stroke pattern.

var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;

var pointStyle = new ol.style.Style({
image: new ol.style.Circle({
    radius: width * 2,
    fill: new ol.style.Fill({
        color: blue
    }),
    stroke: new ol.style.Stroke({
        color: white,
        width: width / 2
    })
}),
zIndex: Infinity
});

var style = function(feature, resolution) {
var widths = [10, 10];
var strokes = [
    [
        {
            width: 4,
            color: '#7af500'
         }, {
             width: 4,
             color: '#55aa00',
             lineDash: [7,9]
         }
    ], [
        {
            width: 2,
            color: 'black'
         }, {
             width: 8,
             color: 'black',
             lineDash: [2,14],
             lineCap: 'butt'
         }
    ]
];
var styles = [pointStyle];
var totalWidth = 0
for (var line = 0; line < widths.length; line++) {
    totalWidth += widths[line];
}
var width = 0;
for (var line = 0; line < widths.length; line++) {
  var dist = (width + widths[line]/2 - totalWidth/2) * resolution;
  width += widths[line];
  var geom = feature.getGeometry();
  if (geom.forEachSegment) {
    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;
        }
    });
    strokes[line].forEach( function(stroke) {
        styles.push(
            new ol.style.Style({
                geometry: new ol.geom.LineString(coords),
                stroke: new ol.style.Stroke(stroke)
            })
        );
    });
  }
}
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,
  style: style,
  type: 'LineString'
}));
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>


来源:https://stackoverflow.com/questions/57421223/openlayers-3-offset-stroke-style

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!