new example

new example
This commit is contained in:
mike-000
2021-05-11 13:42:23 +01:00
parent fdfdc500aa
commit f095ee6ea1
2 changed files with 272 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
---
layout: example.html
title: Scale and Rotate using Modify Interaction
shortdesc: Example of using the Modify interaction to scale and rotate geometries.
docs: >
Example of using the `ol/interaction/Modify` interaction to scale and rotate geometries. Custom style functions produce and display
a scaled and rotated version of the original geometry based on the position of a vertex being modified.
This is set as the final geometry at the end of the interaction.
By default the `ol/geom/Geometry` scale and rotate methods use the center of the geometry extent as anchor.
For irregular shapes the extent changes as the geometry is rotated and using its center as anchor could produce different results
if rotation was stopped and resumed. To avoid that an anchor point which is fixed relative to the geometry is used
- for `ol/geom/Polygon` the centroid of the vertices, and the midpoint for `ol/geom/LineString`.
Only outer vertices (more than 1/3 the maximum distance from the anchor) are used to scale and rotate as precise scaling close to
the anchor would be difficult. For the convenience of the user the style function highlights the anchor and available vertices.
The `ol/interaction/Translate` interaction is also available to reposition geometries.
The Modify and Translate interactions have mutually exclusive `condition` options set so they can be available together.
Use `Ctrl+Drag` (`Command+Drag` on Mac) to use the Translate interaction.
tags: "draw, edit, modify, vector, scale, rotate"
---
<div id="map" class="map"></div>
<form class="form-inline">
<label for="type">Geometry type &nbsp;</label>
<select id="type">
<option value="Point">Point</option>
<option value="LineString">LineString</option>
<option value="Polygon" selected>Polygon</option>
<option value="Circle">Circle</option>
</select>
</form>

View File

@@ -0,0 +1,242 @@
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, Translate} from '../src/ol/interaction.js';
import {MultiPoint, Point} from '../src/ol/geom';
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import {getCenter, getHeight, getWidth} from '../src/ol/extent.js';
import {
never,
platformModifierKeyOnly,
primaryAction,
} from '../src/ol/events/condition.js';
const raster = new TileLayer({
source: new OSM(),
});
const source = new VectorSource();
const style = new Style({
geometry: function (feature) {
const modifyGeometry = feature.get('modifyGeometry');
return modifyGeometry ? modifyGeometry.geometry : feature.getGeometry();
},
fill: new Fill({
color: 'rgba(255, 255, 255, 0.2)',
}),
stroke: new Stroke({
color: '#ffcc33',
width: 2,
}),
image: new CircleStyle({
radius: 7,
fill: new Fill({
color: '#ffcc33',
}),
}),
});
function calculateCenter(geometry) {
let center, coordinates, minRadius;
const type = geometry.getType();
if (type === 'Polygon') {
let x = 0;
let y = 0;
let i = 0;
coordinates = geometry.getCoordinates()[0].slice(1);
coordinates.forEach(function (coordinate) {
x += coordinate[0];
y += coordinate[1];
i++;
});
center = [x / i, y / i];
} else if (type === 'LineString') {
center = geometry.getCoordinateAt(0.5);
coordinates = geometry.getCoordinates();
} else {
center = getCenter(geometry.getExtent());
}
let sqDistances;
if (coordinates) {
sqDistances = coordinates.map(function (coordinate) {
const dx = coordinate[0] - center[0];
const dy = coordinate[1] - center[1];
return dx * dx + dy * dy;
});
minRadius = Math.sqrt(Math.max.apply(Math, sqDistances)) / 3;
} else {
minRadius =
Math.max(
getWidth(geometry.getExtent()),
getHeight(geometry.getExtent())
) / 3;
}
return {
center: center,
coordinates: coordinates,
minRadius: minRadius,
sqDistances: sqDistances,
};
}
const vector = new VectorLayer({
source: source,
style: function (feature) {
const styles = [style];
const modifyGeometry = feature.get('modifyGeometry');
const geometry = modifyGeometry
? modifyGeometry.geometry
: feature.getGeometry();
const result = calculateCenter(geometry);
const center = result.center;
if (center) {
styles.push(
new Style({
geometry: new Point(center),
image: new CircleStyle({
radius: 4,
fill: new Fill({
color: '#ff3333',
}),
}),
})
);
const coordinates = result.coordinates;
if (coordinates) {
const minRadius = result.minRadius;
const sqDistances = result.sqDistances;
const rsq = minRadius * minRadius;
const points = coordinates.filter(function (coordinate, index) {
return sqDistances[index] > rsq;
});
styles.push(
new Style({
geometry: new MultiPoint(points),
image: new CircleStyle({
radius: 4,
fill: new Fill({
color: '#33cc33',
}),
}),
})
);
}
}
return styles;
}
});
const map = new Map({
layers: [raster, vector],
target: 'map',
view: new View({
center: [-11000000, 4600000],
zoom: 4
})
});
const defaultStyle = new Modify({source: source})
.getOverlay()
.getStyleFunction();
const modify = new Modify({
source: source,
condition: function (event) {
return primaryAction(event) && !platformModifierKeyOnly(event);
},
deleteCondition: never,
insertVertexCondition: never,
style: function (feature) {
feature.get('features').forEach(function (modifyFeature) {
let modifyGeometry = modifyFeature.get('modifyGeometry');
if (modifyGeometry) {
const point = feature.getGeometry().getCoordinates();
let modifyPoint = modifyGeometry.point;
if (!modifyPoint) {
// save the initial geometry and vertex position
modifyPoint = point;
modifyGeometry.point = modifyPoint;
modifyGeometry.geometry0 = modifyGeometry.geometry;
// get anchor and minimum radius of vertices to be used
const result = calculateCenter(modifyGeometry.geometry0);
modifyGeometry.center = result.center;
modifyGeometry.minRadius = result.minRadius;
}
const center = modifyGeometry.center;
const minRadius = modifyGeometry.minRadius;
let dx, dy;
dx = modifyPoint[0] - center[0];
dy = modifyPoint[1] - center[1];
const initialRadius = Math.sqrt(dx * dx + dy * dy);
if (initialRadius > minRadius) {
const initialAngle = Math.atan2(dy, dx);
dx = point[0] - center[0];
dy = point[1] - center[1];
const currentRadius = Math.sqrt(dx * dx + dy * dy);
if (currentRadius > 0) {
const currentAngle = Math.atan2(dy, dx);
const geometry = modifyGeometry.geometry0.clone();
geometry.scale(currentRadius / initialRadius, undefined, center);
geometry.rotate(currentAngle - initialAngle, center);
modifyGeometry.geometry = geometry;
}
}
}
});
return defaultStyle(feature);
}
});
modify.on('modifystart', function (event) {
event.features.forEach(function (feature) {
feature.set(
'modifyGeometry',
{geometry: feature.getGeometry().clone()},
true
);
});
});
modify.on('modifyend', function (event) {
event.features.forEach(function (feature) {
const modifyGeometry = feature.get('modifyGeometry');
if (modifyGeometry) {
feature.setGeometry(modifyGeometry.geometry);
feature.unset('modifyGeometry', true);
}
});
});
map.addInteraction(modify);
map.addInteraction(
new Translate({
condition: function (event) {
return primaryAction(event) && platformModifierKeyOnly(event);
},
layers: [vector],
})
);
let draw; // global so we can remove it later
const typeSelect = document.getElementById('type');
function addInteractions() {
draw = new Draw({
source: source,
type: typeSelect.value,
});
map.addInteraction(draw);
}
/**
* Handle change event.
*/
typeSelect.onchange = function () {
map.removeInteraction(draw);
addInteractions();
};
addInteractions();