0
votes

I need to implement a measure tool with OpenLayers, and I would like to display distance on the segment, with a smart management of the scale (a mark on the segment each 10m, then each 50m, 100m, 1km, 5km, for example...), very much like the GoogleMaps "measure distance" tool.

Is there any library doing that ? What would be the good approach to implement it ?

enter image description here

1
I've already implemented and enhanced this example. What I'm looking for is a measuring segment with additional elements to display intermediate values, like a ruler. Like the Google Maps tool. - Zofren
then check this out: gis.stackexchange.com/a/43111/8970 - dube
I appreciate your help but I think you miss my point. I edited my question with a picture, a screenshot of the Google Maps tool I need to have with OpenLayers. - Zofren
Yeah, ruler and segments were too ambiguous... I actually already had an answer for that and trashed it, oops - dube

1 Answers

2
votes

In short: No, I don't know any lib or class that provides what you want out of the box.

Option 1: Customize ScaleLine

ScaleLine (see api docs) has the option to provide your own render function (see code). The default implementation just calculates the distance and shows it as {number} {scale} by calling the internal function updateElement_, which then updates the ScaleLine's innerHtml.

You could theoretically replace that method and set the innerHTML yourself. That approach might limit you to the development-variant of the library, because the production code is minified and those elements (innerElement_, element_) are not marked as api.

new ol.control.ScaleLine({
  render: function(mapEvent) {
     // do stuff
  }
});

Option 2: Use the Draw Feature with customized LineString styles

so that might be too complicated and I suggest you go for the ol.interaction.Draw feature. The Measure Example shows us how one could draw stuff while the user is drawing a line. You can combine that with custom styles on a LineString.

// TODO split the uses drawn line into segments, like this mockup
const line = new ol.geom.LineString([
    [20.0, 50.0],
    [30.0, 47.0],
    [40.0, 47.0],
    [50.0, 47.0]
]);

line.transform('EPSG:4326', 'EPSG:3857');

const lineFeature = new ol.Feature(line);
const lineSource = new ol.source.Vector({
    features: [lineFeature]
});


function segmentText(coord, coord2) {
    const coord_t = ol.proj.transform(coord, 'EPSG:3857', 'EPSG:4326');
    let coordText = coord_t[1].toFixed(0) + '/' + coord_t[0].toFixed(0);
    
    if(coord2) {
      const length = ol.Sphere.getLength(new ol.geom.LineString([coord2, coord]));
      const distance = (Math.round(length / 1000 * 100) / 100) + ' km';
      coordText = coordText + '\n' + distance;
    } else {
      coordText = coordText + '\n0';
    }
    
    return new ol.style.Text({
        text: coordText,
        fill: new ol.style.Fill({
            color: "#00f"
        }),
        offsetY: 25,
        align: 'center',
        scale: 1,
    });
}

function styleFunction(feature) {
    var geometry = feature.getGeometry();
    var styles = [
        // linestring style
        new ol.style.Style({
            stroke: new ol.style.Stroke({
                color: '#ff0000',
                width: 2
            })
        })
    ];

    function createSegmentStyle(coord, coord2, rotation) {
        return new ol.style.Style({
            geometry: new ol.geom.Point(coord),
            image: new ol.style.Icon({
                src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAIAAADdv/LVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAUSURBVBhXY1Da6MPEwMDAxMDAAAALMAEkQYjH8gAAAABJRU5ErkJggg==',
                anchor: [0.75, 0.5],
                rotateWithView: true,
                rotation: -rotation,
                scale: 4
            }),
            text: segmentText(coord, coord2)
        })
    };
    
    const firstCoord = geometry.getFirstCoordinate();
    geometry.forEachSegment(function (start, end) {
        var dx = end[0] - start[0];
        var dy = end[1] - start[1];
        var rotation = Math.atan2(dy, dx);
        if (firstCoord[0] === start[0] && firstCoord[1] === start[1]) {
            styles.push(createSegmentStyle(start, null, rotation));
        }

        styles.push(createSegmentStyle(end, firstCoord, rotation));
    });

    return styles;
}


const map = new ol.Map({
    target: 'map',
    layers: [
        new ol.layer.Tile({
            source: new ol.source.Stamen({ layer:'toner-lite' })
        }),
        new ol.layer.Vector({
            source: lineSource,
            style: styleFunction
        })
    ],
    view: new ol.View({
        center: ol.proj.transform(
            [35, 45], 'EPSG:4326', 'EPSG:3857'),
        zoom: 4
    })
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.5.0/ol.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.5.0/ol-debug.js"></script>
<div id="map" style="height:300px"></div>