英文:
OpenLayers: How can I stop a click from causing a selection when it shouldn't be?
问题
以下是代码部分的中文翻译:
function createRectangle(polygons, layerName) {
const features = [];
for (const [i, polygon] of polygons.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: polygon
},
properties: {
id: `${layerName} 第 ${i} 个`,
layerName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
function createPoints(points, groupName) {
const features = [];
for (const [i, point] of points.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: point
},
properties: {
id: `${groupName} 第 ${i} 个`,
groupName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
const thePoints = createPoints(
[
[50, 33],
[50, 33],
[50, 33],
[50, 33]
],
"thePoints"
);
const styles = {
Point: new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({ color: "red" })
}),
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
}),
Polygon: new ol.style.Style({
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
})
};
const styleFunction = function (feature) {
const featureType = feature.getGeometry().getType();
return styles[featureType];
};
const vectorSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326"
}).readFeatures(thePoints)
});
const cluster = new ol.source.Cluster({
distance: 30,
minDistance: 10,
source: vectorSource
});
const styleCache = {};
const vectorLayer = new ol.layer.Vector({
source: cluster,
style: function (feature) {
const size = feature.get("features").length;
let style = styleCache[size];
if (!style) {
style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: "#fff"
}),
fill: new ol.style.Fill({
color: "#3399CC"
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: "#fff"
})
})
});
styleCache[size] = style;
}
return style;
},
zIndex: 5,
properties: {
name: "big"
}
});
const rectangles = createRectangle(
[
[
[
[-120, -75],
[-120, 75],
[45, 75],
[45, -75]
]
]
],
"rectangle"
);
const rectangleSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326"
}).readFeatures(rectangles)
});
const rectangleLayer = new ol.layer.Vector({
source: rectangleSource,
style: styleFunction,
zIndex: 5
});
const map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer,
rectangleLayer
],
view: new ol.View({
projection: "EPSG:4326",
center: [179, 0],
zoom: 1
})
});
map.on("click", (e) => {
vectorLayer.getFeatures(e.pixel).then((clickedFeatures) => {
if (clickedFeatures.length) {
// 获取聚类坐标
const features = clickedFeatures[0].get("features");
if (features.length > 1) {
e.stopPropagation();
const extent = ol.extent.boundingExtent(
features.map((r) => r.getGeometry().getCoordinates())
);
const extentSize = ol.extent.getSize(extent);
const isSmallExtent = extentSize[0] === 0 && extentSize[1] === 0;
console.log("isSmallExtent", isSmallExtent);
if (isSmallExtent) {
const viewport = map.getViewport();
const boundingRect = viewport.getBoundingClientRect();
console.log("boundingRect", boundingRect);
const centerPoint = [
(boundingRect.left + boundingRect.right) / 2,
(boundingRect.top + boundingRect.bottom) / 2
];
console.log("centerPoint", centerPoint);
map
.getView()
.centerOn(ol.extent.getCenter(extent), map.getSize(), centerPoint);
} else {
map
.getView()
.fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });
}
}
}
});
});
const selected = new ol.style.Style({
fill: new ol.style.Fill({
color: "hsla(270, 50%, 50%, .1)"
}),
stroke: new ol.style.Stroke({
color: "hsla(270, 50%, 50%, 1)",
width: 5
})
});
const selectSingleClick = new ol.interaction.Select({
style: (feature) => {
const color = "hsla(270, 50%, 50%, .1)";
selected.getFill().setColor(color);
return selected;
},
layers: [rectangleLayer],
condition: function (e) {
return (
ol.events.condition.singleClick(e) &&
map.getFeaturesAtPixel(e.pixel, {
layerFilter: function (l) {
return l.getZIndex() > vectorLayer.getZIndex();
}
}).length === 0
);
}
});
selectSingleClick.on("select", function (e) {
console.log("selectSingleClick onSelect");
});
map.addInteraction(selectSingleClick);
#map {
height: 512px;
width: 1024px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.
<details>
<summary>英文:</summary>
To reproduce, click on the blue marker containing the text `4`. Because this is a cluster marker and all of the markers it is clustering are at the same coordinate, it does not make sense to try to zoom in. Instead of zooming in, I center the map on the marker.
However, this click on the 4 marker, is causing a click on the big rectangle to the left, which results in a Select being triggered for the rectangle. This, of course, should not happen.
I have tried telling the event to not propagate if I am doing the centering operation, but that is not helping.
What do I need to change so the Select is not triggered?
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function createRectangle(polygons, layerName) {
const features = [];
for (const [i, polygon] of polygons.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: polygon
},
properties: {
id: `${layerName} index ${i}`,
layerName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
function createPoints(points, groupName) {
const features = [];
for (const [i, point] of points.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: point
},
properties: {
id: `${groupName} index ${i}`,
groupName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
const thePoints = createPoints(
[
[50, 33],
[50, 33],
[50, 33],
[50, 33]
],
"thePoints"
);
const styles = {
Point: new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({ color: "red" })
}),
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
}),
Polygon: new ol.style.Style({
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
})
};
const styleFunction = function (feature) {
const featureType = feature.getGeometry().getType();
return styles[featureType];
};
const vectorSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326" // 4326 3857
}).readFeatures(thePoints)
});
const cluster = new ol.source.Cluster({
distance: 30,
minDistance: 10,
source: vectorSource
});
const styleCache = {};
const vectorLayer = new ol.layer.Vector({
source: cluster,
style: function (feature) {
const size = feature.get("features").length;
let style = styleCache[size];
if (!style) {
style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: "#fff"
}),
fill: new ol.style.Fill({
color: "#3399CC"
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: "#fff"
})
})
});
styleCache[size] = style;
}
return style;
},
zIndex: 5,
properties: {
name: "big"
}
});
const rectangles = createRectangle(
[
[
[
[-120, -75],
[-120, 75],
[45, 75],
[45, -75]
]
]
],
"rectangle"
);
const rectangleSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326" // 4326 3857
}).readFeatures(rectangles)
});
const rectangleLayer = new ol.layer.Vector({
source: rectangleSource,
style: styleFunction,
zIndex: 5
});
const map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer,
rectangleLayer
],
view: new ol.View({
projection: "EPSG:4326",
center: [179, 0],
zoom: 1
})
});
map.on("click", (e) => {
vectorLayer.getFeatures(e.pixel).then((clickedFeatures) => {
if (clickedFeatures.length) {
// Get clustered Coordinates
const features = clickedFeatures[0].get("features");
if (features.length > 1) {
e.stopPropagation();
const extent = ol.extent.boundingExtent(
features.map((r) => r.getGeometry().getCoordinates())
);
const extentSize = ol.extent.getSize(extent);
const isSmallExtent = extentSize[0] === 0 && extentSize[1] === 0;
console.log("isSmallExtent", isSmallExtent);
if (isSmallExtent) {
const viewport = map.getViewport();
const boundingRect = viewport.getBoundingClientRect();
console.log("boundingRect", boundingRect);
const centerPoint = [
(boundingRect.left + boundingRect.right) / 2,
(boundingRect.top + boundingRect.bottom) / 2
];
console.log("centerPoint", centerPoint);
map
.getView()
.centerOn(ol.extent.getCenter(extent), map.getSize(), centerPoint);
} else {
map
.getView()
.fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });
}
}
}
});
});
const selected = new ol.style.Style({
fill: new ol.style.Fill({
color: "hsla(270, 50%, 50%, .1)"
}),
stroke: new ol.style.Stroke({
color: "hsla(270, 50%, 50%, 1)",
width: 5
})
});
const selectSingleClick = new ol.interaction.Select({
style: (feature) => {
const color = "hsla(270, 50%, 50%, .1)";
selected.getFill().setColor(color);
return selected;
},
layers: [rectangleLayer],
condition: function (e) {
return (
ol.events.condition.singleClick(e) &&
map.getFeaturesAtPixel(e.pixel, {
layerFilter: function (l) {
return l.getZIndex() > vectorLayer.getZIndex();
}
}).length === 0
);
}
});
selectSingleClick.on("select", function (e) {
console.log("selectSingleClick onSelect");
});
map.addInteraction(selectSingleClick);
<!-- language: lang-css -->
#map {
height: 512px;
width: 1024px;
}
<!-- language: lang-html -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
<div id="map"></div>
<!-- end snippet -->
</details>
# 答案1
**得分**: 0
以下是代码部分的翻译:
```javascript
You could simply deactivate the select interaction for the duration of a single click (which fires 500ms after the click to ensure it is not a double click)
selectSingleClick.setActive(false);
setTimeout(() => { selectSingleClick.setActive(true); }, 600);
The use of `setTimeout` could be avoided by using a `clickHandled` boolean set only when a map `click` causes some action, and its value being tested in the select condition:
let clickHandled = false;
map.on("click", (e) => {
vectorLayer.getFeatures(e.pixel).then((clickedFeatures) => {
clickHandled = false;
if (clickedFeatures.length) {
// Get clustered Coordinates
const features = clickedFeatures[0].get("features");
clickHandled = true;
const extent = ol.extent.boundingExtent(
features.map((r) => r.getGeometry().getCoordinates())
);
const extentSize = ol.extent.getSize(extent);
const isSmallExtent = extentSize[0] === 0 && extentSize[1] === 0;
console.log("isSmallExtent", isSmallExtent);
if (isSmallExtent) {
const viewport = map.getViewport();
const boundingRect = viewport.getBoundingClientRect();
console.log("boundingRect", boundingRect);
const centerPoint = [
(boundingRect.left + boundingRect.right) / 2,
(boundingRect.top + boundingRect.bottom) / 2
];
console.log("centerPoint", centerPoint);
map
.getView()
.centerOn(ol.extent.getCenter(extent), map.getSize(), centerPoint);
} else {
map
.getView()
.fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });
}
}
});
});
这是你要求的翻译,没有其他内容。
英文:
You could simply deactivate the select interaction for the duration of a single click (which fires 500ms after the click to ensure it is not a double click)
selectSingleClick.setActive(false);
setTimeout(() => { selectSingleClick.setActive(true); }, 600);
<!-- begin snippet: js hide: false console: true babel: null -->
<!-- language: lang-js -->
function createRectangle(polygons, layerName) {
const features = [];
for (const [i, polygon] of polygons.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: polygon
},
properties: {
id: `${layerName} index ${i}`,
layerName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
function createPoints(points, groupName) {
const features = [];
for (const [i, point] of points.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: point
},
properties: {
id: `${groupName} index ${i}`,
groupName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
const thePoints = createPoints(
[
[50, 33],
[50, 33],
[50, 33],
[50, 33]
],
"thePoints"
);
const styles = {
Point: new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({ color: "red" })
}),
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
}),
Polygon: new ol.style.Style({
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
})
};
const styleFunction = function (feature) {
const featureType = feature.getGeometry().getType();
return styles[featureType];
};
const vectorSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326" // 4326 3857
}).readFeatures(thePoints)
});
const cluster = new ol.source.Cluster({
distance: 30,
minDistance: 10,
source: vectorSource
});
const styleCache = {};
const vectorLayer = new ol.layer.Vector({
source: cluster,
style: function (feature) {
const size = feature.get("features").length;
let style = styleCache[size];
if (!style) {
style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: "#fff"
}),
fill: new ol.style.Fill({
color: "#3399CC"
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: "#fff"
})
})
});
styleCache[size] = style;
}
return style;
},
zIndex: 5,
properties: {
name: "big"
}
});
const rectangles = createRectangle(
[
[
[
[-120, -75],
[-120, 75],
[45, 75],
[45, -75]
]
]
],
"rectangle"
);
const rectangleSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326" // 4326 3857
}).readFeatures(rectangles)
});
const rectangleLayer = new ol.layer.Vector({
source: rectangleSource,
style: styleFunction,
zIndex: 5
});
const map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer,
rectangleLayer
],
view: new ol.View({
projection: "EPSG:4326",
center: [179, 0],
zoom: 1
})
});
let selectSingleClick;
map.on("click", (e) => {
vectorLayer.getFeatures(e.pixel).then((clickedFeatures) => {
if (clickedFeatures.length) {
// Get clustered Coordinates
const features = clickedFeatures[0].get("features");
if (features.length > 1) {
selectSingleClick.setActive(false);
setTimeout(() => { selectSingleClick.setActive(true); }, 600);
const extent = ol.extent.boundingExtent(
features.map((r) => r.getGeometry().getCoordinates())
);
const extentSize = ol.extent.getSize(extent);
const isSmallExtent = extentSize[0] === 0 && extentSize[1] === 0;
console.log("isSmallExtent", isSmallExtent);
if (isSmallExtent) {
const viewport = map.getViewport();
const boundingRect = viewport.getBoundingClientRect();
console.log("boundingRect", boundingRect);
const centerPoint = [
(boundingRect.left + boundingRect.right) / 2,
(boundingRect.top + boundingRect.bottom) / 2
];
console.log("centerPoint", centerPoint);
map
.getView()
.centerOn(ol.extent.getCenter(extent), map.getSize(), centerPoint);
} else {
map
.getView()
.fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });
}
}
}
});
});
const selected = new ol.style.Style({
fill: new ol.style.Fill({
color: "hsla(270, 50%, 50%, .1)"
}),
stroke: new ol.style.Stroke({
color: "hsla(270, 50%, 50%, 1)",
width: 5
})
});
selectSingleClick = new ol.interaction.Select({
style: (feature) => {
const color = "hsla(270, 50%, 50%, .1)";
selected.getFill().setColor(color);
return selected;
},
layers: [rectangleLayer],
condition: function (e) {
return (
ol.events.condition.singleClick(e) &&
map.getFeaturesAtPixel(e.pixel, {
layerFilter: function (l) {
return l.getZIndex() > vectorLayer.getZIndex();
}
}).length === 0
);
}
});
selectSingleClick.on("select", function (e) {
console.log("selectSingleClick onSelect");
});
map.addInteraction(selectSingleClick);
<!-- language: lang-css -->
#map {
height: 512px;
width: 1024px;
}
<!-- language: lang-html -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
<div id="map"></div>
<!-- end snippet -->
The use of setTimeout
could be avoided by using a clickHandled
boolean set only when a map click
causes some action, and its value being tested in the select condition:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
function createRectangle(polygons, layerName) {
const features = [];
for (const [i, polygon] of polygons.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: polygon
},
properties: {
id: `${layerName} index ${i}`,
layerName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
function createPoints(points, groupName) {
const features = [];
for (const [i, point] of points.entries()) {
const feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: point
},
properties: {
id: `${groupName} index ${i}`,
groupName
}
};
features.push(feature);
}
const json = {
type: "FeatureCollection",
features
};
return json;
}
const thePoints = createPoints(
[
[50, 33],
[50, 33],
[50, 33],
[50, 33]
],
"thePoints"
);
const styles = {
Point: new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({ color: "red" })
}),
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
}),
Polygon: new ol.style.Style({
stroke: new ol.style.Stroke({
color: "hsla(0, 50%, 100%, 1.0)",
width: 5
}),
fill: new ol.style.Fill({
color: "hsla(0, 50%, 50%, 1.0)"
})
})
};
const styleFunction = function (feature) {
const featureType = feature.getGeometry().getType();
return styles[featureType];
};
const vectorSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326" // 4326 3857
}).readFeatures(thePoints)
});
const cluster = new ol.source.Cluster({
distance: 30,
minDistance: 10,
source: vectorSource
});
const styleCache = {};
const vectorLayer = new ol.layer.Vector({
source: cluster,
style: function (feature) {
const size = feature.get("features").length;
let style = styleCache[size];
if (!style) {
style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: "#fff"
}),
fill: new ol.style.Fill({
color: "#3399CC"
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: "#fff"
})
})
});
styleCache[size] = style;
}
return style;
},
zIndex: 5,
properties: {
name: "big"
}
});
const rectangles = createRectangle(
[
[
[
[-120, -75],
[-120, 75],
[45, 75],
[45, -75]
]
]
],
"rectangle"
);
const rectangleSource = new ol.source.Vector({
features: new ol.format.GeoJSON({
featureProjection: "EPSG:4326" // 4326 3857
}).readFeatures(rectangles)
});
const rectangleLayer = new ol.layer.Vector({
source: rectangleSource,
style: styleFunction,
zIndex: 5
});
const map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer,
rectangleLayer
],
view: new ol.View({
projection: "EPSG:4326",
center: [179, 0],
zoom: 1
})
});
let clickHandled = false;
map.on("click", (e) => {
vectorLayer.getFeatures(e.pixel).then((clickedFeatures) => {
clickHandled = false;
if (clickedFeatures.length) {
// Get clustered Coordinates
const features = clickedFeatures[0].get("features");
if (features.length > 1) {
clickHandled = true;
const extent = ol.extent.boundingExtent(
features.map((r) => r.getGeometry().getCoordinates())
);
const extentSize = ol.extent.getSize(extent);
const isSmallExtent = extentSize[0] === 0 && extentSize[1] === 0;
console.log("isSmallExtent", isSmallExtent);
if (isSmallExtent) {
const viewport = map.getViewport();
const boundingRect = viewport.getBoundingClientRect();
console.log("boundingRect", boundingRect);
const centerPoint = [
(boundingRect.left + boundingRect.right) / 2,
(boundingRect.top + boundingRect.bottom) / 2
];
console.log("centerPoint", centerPoint);
map
.getView()
.centerOn(ol.extent.getCenter(extent), map.getSize(), centerPoint);
} else {
map
.getView()
.fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });
}
}
}
});
});
const selected = new ol.style.Style({
fill: new ol.style.Fill({
color: "hsla(270, 50%, 50%, .1)"
}),
stroke: new ol.style.Stroke({
color: "hsla(270, 50%, 50%, 1)",
width: 5
})
});
selectSingleClick = new ol.interaction.Select({
style: (feature) => {
const color = "hsla(270, 50%, 50%, .1)";
selected.getFill().setColor(color);
return selected;
},
layers: [rectangleLayer],
condition: function (e) {
return (
!clickHandled &&
ol.events.condition.singleClick(e) &&
map.getFeaturesAtPixel(e.pixel, {
layerFilter: function (l) {
return l.getZIndex() > vectorLayer.getZIndex();
}
}).length === 0
);
}
});
selectSingleClick.on("select", function (e) {
console.log("selectSingleClick onSelect");
});
map.addInteraction(selectSingleClick);
<!-- language: lang-css -->
#map {
height: 512px;
width: 1024px;
}
<!-- language: lang-html -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
<div id="map"></div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论