简体   繁体   中英

How to draw an Ellipse using Openlayers3

I'm working on a project where we're using OpenLayers3 .

We need to give users the ability to draw various features on map. Here is an official example to draw features on map.

We need to draw an ellipse on map but there is not such a functionality given officially. I'm wondering if its possible to customize the circle geometry in such a way that we can draw an ellipse on map.

For people that will search this in the future, I hope that It will help. Working code for creating an ellipse in open layer V5.3:

// this works for a static image (pixels) but the idea can be on any coordinate system 
addEllipseFeature(center, semiMajor, semiMinor) {
    let coordinates = [] ;
    const radinas = Math.PI / 180 ;
    for (let angle = 1; angle <= 360; angle ++) {
      const px = center[0] + semiMajor * Math.cos(radinas * angle);
      const py = center[0] + semiMinor * Math.sin(radinas * angle);
      coordinates.push([px, py]);
    }
    const geoJson = {
      'type': 'Feature',
      'geometry': {
        'type': 'Polygon',
        'coordinates': []
      }
    };
    const featureId = source.getFeatures().length;
    coordinates = this.convertCoordinates('Polygon', [coordinates]);
    geoJson.geometry.coordinates = coordinates ;
    const polygonFeature = (new GeoJSON()).readFeature(geoJson);
    polygonFeature.setId(featureId );
    const layerStyle = [  new Style({
      stroke: new Stroke({
        color: 'blue',
        width: 3
      }),
      fill: new Fill({
        color: 'rgba(0, 0, 255, 0.1)'
      })
    })];
    polygonFeature.setStyle(layerStyle);
    source.addFeature(polygonFeature);
    return featureId;
}

在此处输入图片说明

In the draw interaction you can specify a geometryFunction as parameter. This function will draw an additional geometry when you're drawing. That's currently what does the draw interaction in Circle mode, see the code

Have a look tothe draw interaction api for more informations.

Most of the mapping systems don't provide an ellipse geometry or feature. I had the same requirement while working with google maps in Android, the way I solve it is using a polygon by calculating the vertices through the parametric equation of the ellipse.

x = h + a * cos(t)

y = k + b *sin(t)

Take a look to this post to see an example.

Draw a circle, convert it to a circular polygon and then scale the x and y directions. Two clicks are sufficient to define the center and radii of an ellipse along the axes. For an oblique ellipse three clicks would be needed, one for the center, and one click for each radius with the third click also setting the rotation.

 var raster = new ol.layer.Tile({ source: new ol.source.OSM() }); var source = new ol.source.Vector({wrapX: false}); var vector = new ol.layer.Vector({ source: source, }); var map = new ol.Map({ layers: [raster, vector], target: 'map', view: new ol.View({ center: [-11000000, 4600000], zoom: 4 }) }); var typeSelect = document.getElementById('type'); var draw; // global so we can remove it later function addInteraction() { var value = typeSelect.value; var maxPoints; if (value !== 'None') { var geometryFunction; if (value === 'Square') { value = 'Circle'; geometryFunction = ol.interaction.Draw.createRegularPolygon(4); } else if (value === 'Box') { value = 'Circle'; geometryFunction = ol.interaction.Draw.createBox(); } else if (value === 'Star') { value = 'Circle'; geometryFunction = function(coordinates, geometry) { var center = coordinates[0]; var last = coordinates[1]; var dx = center[0] - last[0]; var dy = center[1] - last[1]; var radius = Math.sqrt(dx * dx + dy * dy); var rotation = Math.atan2(dy, dx); var newCoordinates = []; var numPoints = 12; for (var i = 0; i < numPoints; ++i) { var angle = rotation + i * 2 * Math.PI / numPoints; var fraction = i % 2 === 0 ? 1 : 0.5; var offsetX = radius * fraction * Math.cos(angle); var offsetY = radius * fraction * Math.sin(angle); newCoordinates.push([center[0] + offsetX, center[1] + offsetY]); } newCoordinates.push(newCoordinates[0].slice()); if (!geometry) { geometry = new ol.geom.Polygon([newCoordinates]); } else { geometry.setCoordinates([newCoordinates]); } return geometry; }; } else if (value === 'Ellipse') { value = 'Circle'; geometryFunction = function(coordinates, geometry) { var center = coordinates[0]; var last = coordinates[1]; var dx = center[0] - last[0]; var dy = center[1] - last[1]; var radius = Math.sqrt(dx * dx + dy * dy); var circle = new ol.geom.Circle(center, radius); var polygon = ol.geom.Polygon.fromCircle(circle, 64); polygon.scale(dx/radius, dy/radius); if (!geometry) { geometry = polygon; } else { geometry.setCoordinates(polygon.getCoordinates()); } return geometry; }; } else if (value === 'Oblique Ellipse') { value = 'LineString'; maxPoints = 3; geometryFunction = function(coordinates, geometry) { var center = coordinates[0]; var first = coordinates[1]; var dx = center[0] - first[0]; var dy = center[1] - first[1]; var radius1 = Math.sqrt(dx * dx + dy * dy); if (coordinates.length > 2) { var last = coordinates[2]; dx = center[0] - last[0]; dy = center[1] - last[1]; } var radius2 = Math.sqrt(dx * dx + dy * dy); var rotation = Math.atan2(dy, dx); var circle = new ol.geom.Circle(center, radius1); var polygon = ol.geom.Polygon.fromCircle(circle, 64); polygon.scale(radius2/radius1, 1); polygon.rotate(rotation, center); if (!geometry) { geometry = polygon; } else { geometry.setCoordinates(polygon.getCoordinates()); } return geometry; }; } draw = new ol.interaction.Draw({ source: source, type: value, maxPoints: maxPoints, geometryFunction: geometryFunction }); map.addInteraction(draw); } } /** * Handle change event. */ typeSelect.onchange = function() { map.removeInteraction(draw); addInteraction(); }; addInteraction();
 html, body { margin: 0; padding: 0; width: 100%; height: 100%; } .map { width: 100%; height: 90%; }
 <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> <div id="map" class="map"></div> <form class="form-inline"> <label>Shape type &nbsp;</label> <select id="type"> <option value="Ellipse">Ellipse</option> <option value="Oblique Ellipse">Oblique Ellipse</option> <option value="Circle">Circle</option> <option value="Square">Square</option> <option value="Box">Box</option> <option value="Star">Star</option> <option value="None">None</option> </select> </form>

From Mike's code, worked on OL 6.1.1:

const circle = new Circle(
    fromLonLat([longitude, latitude]),
    radius
)

// fromCircle is a function inside Polygon class
const ellipse = fromCircle(circle, 100)
ellipse.scale(1, bigRadius / smallRadius) 

// OL is counterclockwise rotation
ellipse.rotate(-(angleDegree * Math.PI) / 180, circle.getCenter())

this.yourLayer.getSource().addFeature(
    new Feature({
      geometry: ellipse
    })
)

Regards

Thanks to @Ezri YI made a Working example and added more functionality.

look here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM