OpenLayers:用鼠标移动矢量标签
OpenLayers: Move vector label with mouse
我正在使用 OpenLayers V4,我正在尝试查看是否可以允许用户单击特征的矢量标签并将其 move/drag 到他们选择的位置。我最初的想法是在用户点击标签时进行捕捉,然后随着用户鼠标指针的移动动态计算并设置标签的offsetX和offsetY属性(ol.style.Text)。为此,我需要在用户点击标签而不是功能本身时进行捕捉。主要问题是我找不到区分这一点的方法。看起来好像标签是矢量特征的一部分,因为单击特征会突出显示特征和标签,反之亦然。
总而言之,我的问题有两个:
- 有人知道如何在 OpenLayers 4 中创建用户可拖动的矢量标签吗?
- 有没有办法在用户点击矢量要素本身或矢量标签之间 detect/distinguish。
注意:我熟悉叠加层并意识到它们可能更易于使用,因为它们具有 setPosition 属性,但是我的网络地图构建方式需要为每个要素显示矢量标签而不是覆盖
可以在 OpenLayers 6 中使用矢量标签,其中修改交互可以访问其样式函数中正在修改的特征,并且可以使用偏移标签的命中检测,但这在版本 4 中不可用。在此示例中标签随其特征移动,但也可以独立于特征移动。需要克隆以避免在移动标签时更改特征几何形状。然后恢复标签几何形状并用偏移量替换样式。移动要素时,其标签克隆会保持同步。
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script>
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
var modifyStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
});
var labelStyle = new ol.style.Style({
text: new ol.style.Text({
offsetY: 10,
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({
color: '#000',
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 3,
}),
backgroundFill: new ol.style.Fill({
color: 'rgba(0 ,0, 0, 0)',
}),
}),
});
var featureLayer = new ol.layer.Vector({
source: new ol.source.Vector({
url: 'https://mikenunn.net/data/world_cities.geojson',
format: new ol.format.GeoJSON(),
}),
});
var labelLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
renderBuffer: 1e3,
style: function (feature) {
labelStyle.getText().setOffsetX(feature.get('offsetX') || 0);
labelStyle.getText().setOffsetY((feature.get('offsetY') || 0) - 10);
labelStyle.getText().setText(feature.get('CITY_NAME'));
return labelStyle;
},
});
featureLayer.getSource().on('addfeature', function(event) {
var id = event.feature.getId();
var feature = event.feature.clone();
feature.setId(id);
labelLayer.getSource().addFeature(feature);
});
featureLayer.getSource().on('removefeature', function(event) {
var id = event.feature.getId();
var source = labelLayer.getSource();
source.removeFeature(source.getFeatureById(id));
});
var defaultStyle = new ol.interaction.Modify({
source: featureLayer.getSource()
}).getOverlay().getStyleFunction();
var featureModify = new ol.interaction.Modify({
source: featureLayer.getSource(),
style: function(feature) {
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
var geometry = feature.getGeometry().clone();
labelLayer.getSource().getFeatureById(id).setGeometry(geometry);
});
return defaultStyle(feature);
}
});
var labelModify = new ol.interaction.Modify({
source: labelLayer.getSource(),
hitDetection: labelLayer,
style: function(feature) {
var styleFeature;
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
styleGeometry = featureLayer.getSource().getFeatureById(id).getGeometry();
});
modifyStyle.setGeometry(styleGeometry);
return modifyStyle;
}
});
labelModify.on('modifyend', function(event) {
event.features.forEach( function(feature) {
var id = feature.getId();
var labelCoordinates = feature.getGeometry().getCoordinates();
var geometry = featureLayer.getSource().getFeatureById(id).getGeometry().clone();
var featureCoordinates = geometry.getCoordinates();
var resolution = map.getView().getResolution();
var offsetX = (labelCoordinates[0] - featureCoordinates[0]) / resolution + (feature.get('offsetX') || 0);
var offsetY = (featureCoordinates[1] - labelCoordinates[1]) / resolution + (feature.get('offsetY') || 0);
feature.set('offsetX', offsetX, true);
feature.set('offsetY', offsetY, true);
feature.setGeometry(geometry);
});
});
var map = new ol.Map({
layers: [featureLayer, labelLayer],
interactions: ol.interaction.defaults().extend([labelModify, featureModify]),
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([5, 51]),
zoom: 8
})
});
</script>
</body>
</html>
我正在使用 OpenLayers V4,我正在尝试查看是否可以允许用户单击特征的矢量标签并将其 move/drag 到他们选择的位置。我最初的想法是在用户点击标签时进行捕捉,然后随着用户鼠标指针的移动动态计算并设置标签的offsetX和offsetY属性(ol.style.Text)。为此,我需要在用户点击标签而不是功能本身时进行捕捉。主要问题是我找不到区分这一点的方法。看起来好像标签是矢量特征的一部分,因为单击特征会突出显示特征和标签,反之亦然。
总而言之,我的问题有两个:
- 有人知道如何在 OpenLayers 4 中创建用户可拖动的矢量标签吗?
- 有没有办法在用户点击矢量要素本身或矢量标签之间 detect/distinguish。
注意:我熟悉叠加层并意识到它们可能更易于使用,因为它们具有 setPosition 属性,但是我的网络地图构建方式需要为每个要素显示矢量标签而不是覆盖
可以在 OpenLayers 6 中使用矢量标签,其中修改交互可以访问其样式函数中正在修改的特征,并且可以使用偏移标签的命中检测,但这在版本 4 中不可用。在此示例中标签随其特征移动,但也可以独立于特征移动。需要克隆以避免在移动标签时更改特征几何形状。然后恢复标签几何形状并用偏移量替换样式。移动要素时,其标签克隆会保持同步。
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script>
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
var modifyStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
});
var labelStyle = new ol.style.Style({
text: new ol.style.Text({
offsetY: 10,
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({
color: '#000',
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 3,
}),
backgroundFill: new ol.style.Fill({
color: 'rgba(0 ,0, 0, 0)',
}),
}),
});
var featureLayer = new ol.layer.Vector({
source: new ol.source.Vector({
url: 'https://mikenunn.net/data/world_cities.geojson',
format: new ol.format.GeoJSON(),
}),
});
var labelLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
renderBuffer: 1e3,
style: function (feature) {
labelStyle.getText().setOffsetX(feature.get('offsetX') || 0);
labelStyle.getText().setOffsetY((feature.get('offsetY') || 0) - 10);
labelStyle.getText().setText(feature.get('CITY_NAME'));
return labelStyle;
},
});
featureLayer.getSource().on('addfeature', function(event) {
var id = event.feature.getId();
var feature = event.feature.clone();
feature.setId(id);
labelLayer.getSource().addFeature(feature);
});
featureLayer.getSource().on('removefeature', function(event) {
var id = event.feature.getId();
var source = labelLayer.getSource();
source.removeFeature(source.getFeatureById(id));
});
var defaultStyle = new ol.interaction.Modify({
source: featureLayer.getSource()
}).getOverlay().getStyleFunction();
var featureModify = new ol.interaction.Modify({
source: featureLayer.getSource(),
style: function(feature) {
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
var geometry = feature.getGeometry().clone();
labelLayer.getSource().getFeatureById(id).setGeometry(geometry);
});
return defaultStyle(feature);
}
});
var labelModify = new ol.interaction.Modify({
source: labelLayer.getSource(),
hitDetection: labelLayer,
style: function(feature) {
var styleFeature;
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
styleGeometry = featureLayer.getSource().getFeatureById(id).getGeometry();
});
modifyStyle.setGeometry(styleGeometry);
return modifyStyle;
}
});
labelModify.on('modifyend', function(event) {
event.features.forEach( function(feature) {
var id = feature.getId();
var labelCoordinates = feature.getGeometry().getCoordinates();
var geometry = featureLayer.getSource().getFeatureById(id).getGeometry().clone();
var featureCoordinates = geometry.getCoordinates();
var resolution = map.getView().getResolution();
var offsetX = (labelCoordinates[0] - featureCoordinates[0]) / resolution + (feature.get('offsetX') || 0);
var offsetY = (featureCoordinates[1] - labelCoordinates[1]) / resolution + (feature.get('offsetY') || 0);
feature.set('offsetX', offsetX, true);
feature.set('offsetY', offsetY, true);
feature.setGeometry(geometry);
});
});
var map = new ol.Map({
layers: [featureLayer, labelLayer],
interactions: ol.interaction.defaults().extend([labelModify, featureModify]),
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([5, 51]),
zoom: 8
})
});
</script>
</body>
</html>