From f33b9587fb5abb4cffb36e68ec2943a7c8a80960 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Fri, 23 Apr 2021 13:04:06 +0100 Subject: [PATCH] New example --- examples/draw-and-modify-geodesic.html | 25 ++++ examples/draw-and-modify-geodesic.js | 184 +++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 examples/draw-and-modify-geodesic.html create mode 100644 examples/draw-and-modify-geodesic.js diff --git a/examples/draw-and-modify-geodesic.html b/examples/draw-and-modify-geodesic.html new file mode 100644 index 0000000000..a5c7ffcf6a --- /dev/null +++ b/examples/draw-and-modify-geodesic.html @@ -0,0 +1,25 @@ +--- +layout: example.html +title: Draw and Modify Geodesic Circles +shortdesc: Example of using Draw and Modify interactions for geodesic circles. +docs: > + Example of using the `ol/interaction/Draw` interaction with a custom geometry function together with the `ol/interaction/Modify` interaction + to draw and modify geodesic circles (a `ol/geom/Polygon#circular` polygon representing a circle on the surface of the Earth's sphere). + The polygon is placed in a `ol/geom/GeometryCollection` together with a `ol/geom/Point` which allows the Modify interaction to adjust the + circle center as well as the radius. Custom style functions ensure the correct final geometry is displayed throughout. + `ol/geom/Circle` projected (planar) geometries can also be drawn and modified. The difference between geodesic and projected circles can be + seen when their centers are moved between northern and southern latitudes in the Web Mercator projection. + The `ol/interaction/Snap` interaction can be used to create concentric circles. +tags: "draw, edit, modify, vector, circle, sphere, geodesic" +--- +
+
+ + +
diff --git a/examples/draw-and-modify-geodesic.js b/examples/draw-and-modify-geodesic.js new file mode 100644 index 0000000000..4a61fa58d1 --- /dev/null +++ b/examples/draw-and-modify-geodesic.js @@ -0,0 +1,184 @@ +import Map from '../src/ol/Map.js'; +import View from '../src/ol/View.js'; +import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js'; +import {Draw, Modify, Snap} from '../src/ol/interaction.js'; +import {GeometryCollection, Point, Polygon} from '../src/ol/geom.js'; +import {OSM, Vector as VectorSource} from '../src/ol/source.js'; +import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js'; +import {circular} from '../src/ol/geom/Polygon.js'; +import {getDistance} from '../src/ol/sphere.js'; +import {transform} from '../src/ol/proj.js'; + +const raster = new TileLayer({ + source: new OSM(), +}); + +const source = new VectorSource(); + +const style = new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)', + }), + stroke: new Stroke({ + color: '#33cc33', + width: 2, + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: '#ffcc33', + }), + }), +}); + +const geodesicStyle = new Style({ + geometry: function (feature) { + return feature.get('modifyGeometry') || feature.getGeometry(); + }, + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)', + }), + stroke: new Stroke({ + color: '#ff3333', + width: 2, + }), + image: new CircleStyle({ + radius: 7, + fill: new Fill({ + color: 'rgba(0, 0, 0, 0)', + }), + }), +}); + +const vector = new VectorLayer({ + source: source, + style: function (feature) { + const geometry = feature.getGeometry(); + return geometry.getType() === 'GeometryCollection' ? geodesicStyle : style; + }, +}); + +const map = new Map({ + layers: [raster, vector], + target: 'map', + view: new View({ + center: [-11000000, 6600000], + zoom: 3, + }), +}); + +const defaultStyle = new Modify({source: source}) + .getOverlay() + .getStyleFunction(); + +const modify = new Modify({ + source: source, + style: function (feature) { + feature.get('features').forEach(function (modifyFeature) { + const modifyGeometry = modifyFeature.get('modifyGeometry'); + if (modifyGeometry) { + const modifyPoint = feature.getGeometry().getCoordinates(); + const geometries = modifyFeature.getGeometry().getGeometries(); + const polygon = geometries[0].getCoordinates()[0]; + const center = geometries[1].getCoordinates(); + const projection = map.getView().getProjection(); + let first, last, radius; + if (modifyPoint[0] === center[0] && modifyPoint[1] === center[1]) { + // center is being modified + // get unchanged radius from diameter between polygon vertices + first = transform(polygon[0], projection, 'EPSG:4326'); + last = transform( + polygon[(polygon.length - 1) / 2], + projection, + 'EPSG:4326' + ); + radius = getDistance(first, last) / 2; + } else { + // radius is being modified + first = transform(center, projection, 'EPSG:4326'); + last = transform(modifyPoint, projection, 'EPSG:4326'); + radius = getDistance(first, last); + } + // update the polygon using new center or radius + const circle = circular( + transform(center, projection, 'EPSG:4326'), + radius, + 128 + ); + circle.transform('EPSG:4326', projection); + geometries[0].setCoordinates(circle.getCoordinates()); + // save changes to be applied at the end of the interaction + modifyGeometry.setGeometries(geometries); + } + }); + return defaultStyle(feature); + }, +}); + +modify.on('modifystart', function (event) { + event.features.forEach(function (feature) { + const geometry = feature.getGeometry(); + if (geometry.getType() === 'GeometryCollection') { + feature.set('modifyGeometry', geometry.clone(), true); + } + }); +}); + +modify.on('modifyend', function (event) { + event.features.forEach(function (feature) { + const modifyGeometry = feature.get('modifyGeometry'); + if (modifyGeometry) { + feature.setGeometry(modifyGeometry); + feature.unset('modifyGeometry', true); + } + }); +}); + +map.addInteraction(modify); + +let draw, snap; // global so we can remove them later +const typeSelect = document.getElementById('type'); + +function addInteractions() { + let value = typeSelect.value; + let geometryFunction; + if (value === 'Geodesic') { + value = 'Circle'; + geometryFunction = function (coordinates, geometry, projection) { + if (!geometry) { + geometry = new GeometryCollection([ + new Polygon([]), + new Point(coordinates[0]), + ]); + } + const geometries = geometry.getGeometries(); + const center = transform(coordinates[0], projection, 'EPSG:4326'); + const last = transform(coordinates[1], projection, 'EPSG:4326'); + const radius = getDistance(center, last); + const circle = circular(center, radius, 128); + circle.transform('EPSG:4326', projection); + geometries[0].setCoordinates(circle.getCoordinates()); + geometry.setGeometries(geometries); + return geometry; + }; + } + draw = new Draw({ + source: source, + type: value, + geometryFunction: geometryFunction, + }); + map.addInteraction(draw); + snap = new Snap({source: source}); + map.addInteraction(snap); +} + +/** + * Handle change event. + */ +typeSelect.onchange = function () { + map.removeInteraction(draw); + map.removeInteraction(snap); + addInteractions(); +}; + +addInteractions();