Search code examples
openlayersopenlayers-5

In Openlayers, how can I scale the stroke width of a MultiLineString based on the number of times the same coordinates are passed through?


My MultiLineString currently is styled like this:

'MultiLineString': new Style({
    stroke: new Stroke({
        color: 'green',
        width: 1
    })
}),

And the coordinates for my MultiLineString looks something like this:

[
[[3, 8], [4, 10], [5, 12]],
[[2, 4], [3, 8], [4, 10]]
]

where there is some overlap in the data. I want to scale the width of the line where the data overlaps to help illustrate that the line is being gone over multiple times. I'm guessing I need to utilize the RenderFunction(), but I'm having a hard time understanding what to do next.

Any help would be greatly appreciated.


Solution

  • Yes, it can be done using Turf but Turf only works on EPSG:4326 coordinates and what overlaps in EPSG:4326 won't necessarily overlap when displayed in EPSG:3857 and vice versa, for example [[0,50],[5,55],[10,60]] isn't quite a straight line when transformed to EPSG:3857 while [[0,50],[10,60]] is. Transforms also introduce very small rounding differences. Although very small any difference will stop overlaps being detected (and using Turf's km tolerance setting can give false positives). This example does the extra iterations, and adds an extra component to your MultiLineString to test intersection between the vertices of longer line segments. After rounding to 12 places an overlap is detected in EPSG:4326 (with real world fractional values even that might not work) but if you zoom in enough you can see it is a parallel line in EPSG:3857. What is really needed is a function based on the Turf lineOverlap source which will work on any coordinates and handles mm tolerances appropriate to those coordinates.

    var transformR = function(coordinates, output, dimensions) {
      var dims = dimensions || 2;
      for (var i=0; i<coordinates.length; i+=dims) {
    coordinates[i] = Math.round(coordinates[i]*1e12)/1e12;
    coordinates[i+1] = Math.round(coordinates[i+1]*1e12)/1e12;
      }
      return coordinates;
    }
    
    var style = function(feature) {
    
      switch(feature.getGeometry().getType()) {
    case 'MultiLineString': 
    
      var increment = 2;
      var styles = [
        new ol.style.Style({
          stroke: new ol.style.Stroke({
            color: 'green',
            width: increment
          })
        })
      ];
    
      var overlaps = [];
      var format = new ol.format.GeoJSON();
      var geometry = feature.getGeometry().clone().transform(map.getView().getProjection(), 'EPSG:4326');
      geometry.applyTransform(transformR); // round transformed coordinates
      var linestrings = geometry.getLineStrings();
      for (var i=0; i<linestrings.length-1; i++) {
        for (var j=i+1; j<linestrings.length; j++) {
          var line1 = format.writeFeatureObject(new ol.Feature(linestrings[i]));
          var line2 = format.writeFeatureObject(new ol.Feature(linestrings[j]));
          var overlapping = turf.lineOverlap(line1, line2).features;
          overlapping.forEach(function(overlap){ overlaps.push(overlap.geometry.coordinates); });
        }
      }
      overlaps.forEach(function(overlap){
        var width = increment;
        var line = turf.lineString(overlap);
        for (var i=0; i<linestrings.length; i++) {
          var line1 = format.writeFeatureObject(new ol.Feature(linestrings[i]));
          var overlapping = turf.lineOverlap(line, line1).features;
          if (overlapping.length > 0 && JSON.stringify(overlapping[0].geometry.coordinates) == JSON.stringify(overlap)) {
            width += increment;
          }
        }
        styles.push(
          new ol.style.Style({
            geometry: new ol.geom.LineString(overlap).transform('EPSG:4326', map.getView().getProjection()),
            stroke: new ol.style.Stroke({
              color: 'green',
              width: width
            })
          })
        );
      });
    
      return styles;
    
      }
    
    }
    
    var multiline = new ol.Feature(new ol.geom.MultiLineString([
      [[6, 14], [2, 6]],
      [[3, 8], [4, 10], [5, 12]],
      [[2, 4], [3, 8], [4, 10]]
    ]));
    
    var source = new ol.source.Vector();
    
    var map = new ol.Map({
    layers: [
        new ol.layer.Vector({
            source: source,
            style: style
        })
    ],
    target: 'map',
    view: new ol.View()
    });
    
    multiline.getGeometry().transform('EPSG:4326', map.getView().getProjection());
    source.addFeature(multiline);
    
    map.getView().fit(multiline.getGeometry());
    <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://npmcdn.com/@turf/[email protected]/turf.min.js"></script>
    
    <div id="map" class="map"></div>