2

The distance between Ustica Island and the coast of Palermo is about 54 thousand meters. I expect that by constructing a Circle like this new ol.geom.Circle(centerInMapProjection, 53708); to see the perimeter of the circle touch the coast. Yet the circle is much smaller. What am I doing wrong?

enter image description here

Here is a working snippet with OpenLayers 3. It also has the measurement system built in.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OpenLayers Circle and Geodetic Measurement</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v3.20.1/css/ol.css" type="text/css">
    <style>
        #map {
            width: 100%;
            height: 100vh;
        }
        .ol-tooltip {
            background-color: white;
            border: 1px solid black;
            padding: 4px;
            position: absolute;
            white-space: nowrap;
        }
        .ol-tooltip-measure {
            opacity: 0.7;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div id="map" class="map"></div>
    <script src="https://openlayers.org/en/v3.20.1/build/ol.js"></script>
    <script>
        var map = new ol.Map({
            target: 'map',
            layers: [
                new ol.layer.Tile({
                    source: new ol.source.OSM()
                })
            ],
            view: new ol.View({
                center: [1468335.0056440544, 4680137.027803559],
                zoom: 7
            })
        });

        function addCircle(x, y, radiusInMeters) {
            var centerInMapProjection = [x, y];

            var circleGeometry = new ol.geom.Circle(centerInMapProjection, radiusInMeters);

            var circleFeature = new ol.Feature(circleGeometry);

            var vectorSource = new ol.source.Vector({
                features: [circleFeature]
            });

            var vectorLayer = new ol.layer.Vector({
                source: vectorSource,
                style: new ol.style.Style({
                    stroke: new ol.style.Stroke({
                        color: 'red',
                        width: 2
                    }),
                    fill: new ol.style.Fill({
                        color: 'rgba(255, 0, 0, 0.2)'
                    })
                })
            });

            map.addLayer(vectorLayer);
        }

        addCircle(1468335.0056440544, 4680137.027803559, 53708);
        
        var measureSource = new ol.source.Vector();
        
        var measureLayer = new ol.layer.Vector({
            source: measureSource,
            style: new ol.style.Style({
                fill: new ol.style.Fill({
                    color: 'rgba(255, 255, 255, 0.2)'
                }),
                stroke: new ol.style.Stroke({
                    color: '#ffcc33',
                    width: 2
                }),
                image: new ol.style.Circle({
                    radius: 7,
                    fill: new ol.style.Fill({
                        color: '#ffcc33'
                    })
                })
            })
        });

        map.addLayer(measureLayer);

        var draw = new ol.interaction.Draw({
            source: measureSource,
            type: 'LineString',
            style: new ol.style.Style({
                fill: new ol.style.Fill({
                    color: 'rgba(255, 255, 255, 0.2)'
                }),
                stroke: new ol.style.Stroke({
                    color: 'rgba(0, 0, 0, 0.5)',
                    lineDash: [10, 10],
                    width: 2
                }),
                image: new ol.style.Circle({
                    radius: 5,
                    stroke: new ol.style.Stroke({
                        color: 'rgba(0, 0, 0, 0.7)'
                    }),
                    fill: new ol.style.Fill({
                        color: 'rgba(255, 255, 255, 0.2)'
                    })
                })
            })
        });
        map.addInteraction(draw);

        var measureTooltipElement = document.createElement('div');
        measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure';
        var measureTooltip = new ol.Overlay({
            element: measureTooltipElement,
            offset: [0, -15],
            positioning: 'bottom-center'
        });
        map.addOverlay(measureTooltip);

        var formatLength = function(line) {
            var wgs84Sphere = new ol.Sphere(6378137);
            var coordinates = line.getCoordinates();
            var length = 0;
            var sourceProj = map.getView().getProjection();
            for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
                var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
                length += wgs84Sphere.haversineDistance(c1, c2);
            }
            var output;
            /*if (length > 100) {
                output = (Math.round(length / 1000 * 100) / 100) + ' km';
            } else {*/
                output = (Math.round(length * 100) / 100) + ' m';
            //}
            return output;
        };
   
        draw.on('drawstart', function(evt) {
            measureSource.clear();
            var tooltipCoord = evt.coordinate;
            evt.feature.getGeometry().on('change', function(evt) {
                var geom = evt.target;
                var output = formatLength(geom);
                tooltipCoord = geom.getLastCoordinate();
                measureTooltipElement.innerHTML = output;
                measureTooltip.setPosition(tooltipCoord);
            });
        });

        draw.on('drawend', function() {
            measureTooltipElement = null;
            createMeasureTooltip();
        });

        function createMeasureTooltip() {
            measureTooltipElement = document.createElement('div');
            measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure';
            measureTooltip = new ol.Overlay({
                element: measureTooltipElement,
                offset: [20, -25],
                positioning: 'bottom-center'
            });
            map.addOverlay(measureTooltip);
        }

        createMeasureTooltip();
    </script>
</body>
</html>

1 Answer 1

6

Circle geometry uses projection units as radius, and in EPSG:3857 those are equal to distance on the ground only at the equator. For a small circles you could adjust the radius for the local point resolution

var pointResolution =  ol.proj.getPointResolution(map.getView().getProjection(), 1, centerInMapProjection);
var circleGeometry = new ol.geom.Circle(centerInMapProjection, radiusInMeters / pointResolution);

For a large radius the circle on the ground may not even look like a circle in the view projection at high latitudes (see https://openlayers.org/en/latest/examples/draw-and-modify-geodesic.html) and you would get a more accurate result using ol.geom.Polygon.circular.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.