如何访问重叠 geojson/polygons 以显示在信息框中
How to access overlapping geojson/polygons to display in infobox
我正在使用 Google 地图 API 使用 geoJSON 数据层将多个多边形加载到地图中。其中一些多边形在某些区域重叠。当用户单击多个多边形内部的点时,我想在带有单击事件的信息框中显示属性(名称、标签等)。
我想显示给定点的所有多边形的属性。目前,当我点击一个点时,我只能看到一个多边形,尽管该点在多个多边形内部。
如何使用 Google 地图 API v3 访问所有多边形的所有属性?
const map = useGoogleMap(); // google map instance
const polygons; // an array of polygons, example snippet below.
map.data.addGeoJson(polygons);
map.data.addListener('click', function(event) {
// how can i access other features underneath this clicked point
console.log(event.feature); // only returns "Geofence 1"
})
GeoJson 示例:
polygons = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Geofence 1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-8.96484375,
-9.96885060854611
],
[
3.955078125,
-9.96885060854611
],
[
3.955078125,
-0.17578097424708533
],
[
-8.96484375,
-0.17578097424708533
],
[
-8.96484375,
-9.96885060854611
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-6.591796875,
-8.320212289522944
],
[
2.197265625,
-8.320212289522944
],
[
2.197265625,
-1.9332268264771106
],
[
-6.591796875,
-1.9332268264771106
],
[
-6.591796875,
-8.320212289522944
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 3"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-4.39453125,
-6.926426847059551
],
[
0.263671875,
-6.926426847059551
],
[
0.263671875,
-3.337953961416472
],
[
-4.39453125,
-3.337953961416472
],
[
-4.39453125,
-6.926426847059551
]
]
]
}
}
]
}
一种选择是使用几何库中的 containsLocation 方法。
containsLocation(point, polygon)
Parameters:
point: LatLng
polygon: Polygon
Return Value: boolean
Computes whether the given point lies inside the specified polygon.
不幸的是,它只适用于本机 google.maps.Polygon
对象而不是 Data.Polygon
对象。将要素中的数据转换为原生 google.maps.Polygon
对象,将它们推送到数组中,然后处理数组以查看点击位于哪个多边形中。
- 为输入中的每个多边形创建
google.maps.Polygon
(假设只有多边形)
var polygonArray = [];
map.data.addListener('addfeature', function(e) {
e.feature.getGeometry().getArray().forEach(function(latLngArry){
const polygon = new google.maps.Polygon({
map: map,
paths: latLngArry.getArray(),
clickable: false,
name: e.feature.getProperty("name") // save the data we want to output as an attribute
})
polygonArray.push(polygon);
})
- 点击时检查点击所在的多边形:
map.addListener('click', function(event) {
var content = "";
for (var i=0;i<polygonArray.length;i++) {
if (google.maps.geometry.poly.containsLocation(event.latLng, polygonArray[i])) {
if (content.length!=0)
content+=" : "
content += polygonArray[i].name;
}
}
console.log(content);
})
// This example uses the Google Maps JavaScript API's Data layer
// to create a rectangular polygon with 2 holes in it.
function initMap() {
const map = new google.maps.Map(document.getElementById("map"));
const infowindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
var polygonArray = [];
map.data.addListener('addfeature', function(e) {
console.log(e.feature.getGeometry().getArray().length);
e.feature.getGeometry().getArray().forEach(function(latLngArry) {
console.log(latLngArry.getArray())
const polygon = new google.maps.Polygon({
map: map,
paths: latLngArry.getArray(),
clickable: false,
name: e.feature.getProperty("name")
})
polygonArray.push(polygon);
})
processPoints(e.feature.getGeometry(), bounds.extend, bounds);
map.fitBounds(bounds);
});
const features = map.data.addGeoJson(polygons);
map.data.setMap(null);
map.addListener('click', function(event) {
var content = "";
for (var i = 0; i < polygonArray.length; i++) {
if (google.maps.geometry.poly.containsLocation(event.latLng, polygonArray[i])) {
if (content.length != 0)
content += " : "
content += polygonArray[i].name;
}
}
console.log(content);
document.getElementById('info').innerHTML = content;
infowindow.setPosition(event.latLng);
if (content.length == 0) content = "no GeoFence";
infowindow.setContent(content);
infowindow.open(map);
})
function processPoints(geometry, callback, thisArg) {
if (geometry instanceof google.maps.LatLng) {
callback.call(thisArg, geometry);
} else if (geometry instanceof google.maps.Data.Point) {
callback.call(thisArg, geometry.get());
} else {
geometry.getArray().forEach(function(g) {
processPoints(g, callback, thisArg);
});
}
}
}
const polygons = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"name": "Geofence 1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-8.96484375, -9.96885060854611],
[
3.955078125, -9.96885060854611
],
[
3.955078125, -0.17578097424708533
],
[-8.96484375, -0.17578097424708533],
[-8.96484375, -9.96885060854611]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-6.591796875, -8.320212289522944],
[
2.197265625, -8.320212289522944
],
[
2.197265625, -1.9332268264771106
],
[-6.591796875, -1.9332268264771106],
[-6.591796875, -8.320212289522944]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 3"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-4.39453125, -6.926426847059551],
[
0.263671875, -6.926426847059551
],
[
0.263671875, -3.337953961416472
],
[-4.39453125, -3.337953961416472],
[-4.39453125, -6.926426847059551]
]
]
}
}
]
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 90%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<title>Data Layer: Polygon</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly" defer></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="info"></div>
<div id="map"></div>
</body>
</html>
我能够让这个 POC 与 React 一起工作,并分享给下一个可能感兴趣的人:
import { InfoWindow, useGoogleMap } from '@react-google-maps/api';
export const GeofencesContainer = () => {
const map = useGoogleMap();
// geofence = geoJSON feature collections
const [geofenceClickEventLatLng, setGeofenceClickEventLatLng] = useState({});
const [geofencesProperties, setGeofencesProperties] = useState([]);
const [isInfoWindowOpen, setIsInfoWindowOpen] = useState(false);
const [polygonArray, setPolygonArray] = useState([]);
const ref = useRef();
useEffect(() => {
let addFeatureListener;
const getGeofences = async () => {
if (true) {
if (ref.current !== 'geofenceEnabled') {
// get geofences from api
const geofences = await fetch.getGeofences();
// a listener to add feature, where we create a google.maps.polygon for every added feature, then add it to the polygon array
const addedPolygons = [];
addFeatureListener = map.data.addListener('addfeature', e => {
const featureGeometry = e.feature.getGeometry().getArray();
// for each geometry get the latLng array, which will be used to form a polygon
featureGeometry.forEach(latLngArray => {
const polygon = new window.google.maps.Polygon({
map,
paths: latLngArray.getArray(),
strokeWeight: 1,
fillColor: 'green',
clickable: false,
name: e.feature.getProperty('name')
});
addedPolygons.push(polygon);
});
setPolygonArray(addedPolygons);
});
// add the polygon to the map data layer (this will show the polygon on the map and cause the addfeature listener to fire)
map.data.addGeoJson(geofences);
map.data.setStyle({ fillColor: 'green', strokeWeight: 1 });
// we set map data to null so that we don't end up with 2 polygons on top of each other
map.data.setMap(null);
ref.current = 'geofenceEnabled';
}
} else {
ref.current = null;
for (const polygon of polygonArray) {
polygon.setMap(null);
}
setIsInfoWindowOpen(false);
}
};
getGeofences();
return () => {
if (addFeatureListener) {
addFeatureListener.remove();
}
};
}, [activeKmls]);
useEffect(() => {
let clickListener;
if (true) {
// a listener on click that checks whether the point is in a polygon and updates the necessary state to show the proper info window
clickListener = map.addListener('click', function(event) {
// this state is updated to identify the place where the info window will open
setGeofenceClickEventLatLng(event.latLng);
// for every polygon in the created polygons array check if it includes the clicked point, then update what to display in the info window
const selectedGeofences = [];
for (const polygon of polygonArray) {
if (window.google.maps.geometry.poly.containsLocation(event.latLng, polygon)) {
const name = polygon.name;
selectedGeofences.push({ key: name, name });
}
}
if (selectedGeofences.length) {
setIsInfoWindowOpen(true);
} else {
setIsInfoWindowOpen(false);
}
setGeofencesProperties(selectedGeofences);
});
}
return () => {
if (clickListener) {
clickListener.remove();
}
};
}, [activeKmls, polygonArray, altitudeUnit, map]);
return (
<Fragment>
{isInfoWindowOpen && (
<InfoWindow
position={geofenceClickEventLatLng}
onCloseClick={() => setIsInfoWindowOpen(false)}
zIndex={0}
options={{ maxWidth: '450' }}
>
<Fragment>
{geofencesProperties.map(geofenceProperties => {
return (
<Fragment key={geofenceProperties.key}>
<Title level={4}>{geofenceProperties.name}</Title>
</Fragment>
);
})}
</Fragment>
</InfoWindow>
)}
</Fragment>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
我正在使用 Google 地图 API 使用 geoJSON 数据层将多个多边形加载到地图中。其中一些多边形在某些区域重叠。当用户单击多个多边形内部的点时,我想在带有单击事件的信息框中显示属性(名称、标签等)。
我想显示给定点的所有多边形的属性。目前,当我点击一个点时,我只能看到一个多边形,尽管该点在多个多边形内部。
如何使用 Google 地图 API v3 访问所有多边形的所有属性?
const map = useGoogleMap(); // google map instance
const polygons; // an array of polygons, example snippet below.
map.data.addGeoJson(polygons);
map.data.addListener('click', function(event) {
// how can i access other features underneath this clicked point
console.log(event.feature); // only returns "Geofence 1"
})
GeoJson 示例:
polygons = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Geofence 1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-8.96484375,
-9.96885060854611
],
[
3.955078125,
-9.96885060854611
],
[
3.955078125,
-0.17578097424708533
],
[
-8.96484375,
-0.17578097424708533
],
[
-8.96484375,
-9.96885060854611
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-6.591796875,
-8.320212289522944
],
[
2.197265625,
-8.320212289522944
],
[
2.197265625,
-1.9332268264771106
],
[
-6.591796875,
-1.9332268264771106
],
[
-6.591796875,
-8.320212289522944
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 3"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-4.39453125,
-6.926426847059551
],
[
0.263671875,
-6.926426847059551
],
[
0.263671875,
-3.337953961416472
],
[
-4.39453125,
-3.337953961416472
],
[
-4.39453125,
-6.926426847059551
]
]
]
}
}
]
}
一种选择是使用几何库中的 containsLocation 方法。
containsLocation(point, polygon) Parameters:
point: LatLng
polygon: Polygon
Return Value: boolean
Computes whether the given point lies inside the specified polygon.
不幸的是,它只适用于本机 google.maps.Polygon
对象而不是 Data.Polygon
对象。将要素中的数据转换为原生 google.maps.Polygon
对象,将它们推送到数组中,然后处理数组以查看点击位于哪个多边形中。
- 为输入中的每个多边形创建
google.maps.Polygon
(假设只有多边形)
var polygonArray = [];
map.data.addListener('addfeature', function(e) {
e.feature.getGeometry().getArray().forEach(function(latLngArry){
const polygon = new google.maps.Polygon({
map: map,
paths: latLngArry.getArray(),
clickable: false,
name: e.feature.getProperty("name") // save the data we want to output as an attribute
})
polygonArray.push(polygon);
})
- 点击时检查点击所在的多边形:
map.addListener('click', function(event) {
var content = "";
for (var i=0;i<polygonArray.length;i++) {
if (google.maps.geometry.poly.containsLocation(event.latLng, polygonArray[i])) {
if (content.length!=0)
content+=" : "
content += polygonArray[i].name;
}
}
console.log(content);
})
// This example uses the Google Maps JavaScript API's Data layer
// to create a rectangular polygon with 2 holes in it.
function initMap() {
const map = new google.maps.Map(document.getElementById("map"));
const infowindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
var polygonArray = [];
map.data.addListener('addfeature', function(e) {
console.log(e.feature.getGeometry().getArray().length);
e.feature.getGeometry().getArray().forEach(function(latLngArry) {
console.log(latLngArry.getArray())
const polygon = new google.maps.Polygon({
map: map,
paths: latLngArry.getArray(),
clickable: false,
name: e.feature.getProperty("name")
})
polygonArray.push(polygon);
})
processPoints(e.feature.getGeometry(), bounds.extend, bounds);
map.fitBounds(bounds);
});
const features = map.data.addGeoJson(polygons);
map.data.setMap(null);
map.addListener('click', function(event) {
var content = "";
for (var i = 0; i < polygonArray.length; i++) {
if (google.maps.geometry.poly.containsLocation(event.latLng, polygonArray[i])) {
if (content.length != 0)
content += " : "
content += polygonArray[i].name;
}
}
console.log(content);
document.getElementById('info').innerHTML = content;
infowindow.setPosition(event.latLng);
if (content.length == 0) content = "no GeoFence";
infowindow.setContent(content);
infowindow.open(map);
})
function processPoints(geometry, callback, thisArg) {
if (geometry instanceof google.maps.LatLng) {
callback.call(thisArg, geometry);
} else if (geometry instanceof google.maps.Data.Point) {
callback.call(thisArg, geometry.get());
} else {
geometry.getArray().forEach(function(g) {
processPoints(g, callback, thisArg);
});
}
}
}
const polygons = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"name": "Geofence 1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-8.96484375, -9.96885060854611],
[
3.955078125, -9.96885060854611
],
[
3.955078125, -0.17578097424708533
],
[-8.96484375, -0.17578097424708533],
[-8.96484375, -9.96885060854611]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-6.591796875, -8.320212289522944],
[
2.197265625, -8.320212289522944
],
[
2.197265625, -1.9332268264771106
],
[-6.591796875, -1.9332268264771106],
[-6.591796875, -8.320212289522944]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 3"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-4.39453125, -6.926426847059551],
[
0.263671875, -6.926426847059551
],
[
0.263671875, -3.337953961416472
],
[-4.39453125, -3.337953961416472],
[-4.39453125, -6.926426847059551]
]
]
}
}
]
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 90%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<title>Data Layer: Polygon</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly" defer></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="info"></div>
<div id="map"></div>
</body>
</html>
我能够让这个 POC 与 React 一起工作,并分享给下一个可能感兴趣的人:
import { InfoWindow, useGoogleMap } from '@react-google-maps/api';
export const GeofencesContainer = () => {
const map = useGoogleMap();
// geofence = geoJSON feature collections
const [geofenceClickEventLatLng, setGeofenceClickEventLatLng] = useState({});
const [geofencesProperties, setGeofencesProperties] = useState([]);
const [isInfoWindowOpen, setIsInfoWindowOpen] = useState(false);
const [polygonArray, setPolygonArray] = useState([]);
const ref = useRef();
useEffect(() => {
let addFeatureListener;
const getGeofences = async () => {
if (true) {
if (ref.current !== 'geofenceEnabled') {
// get geofences from api
const geofences = await fetch.getGeofences();
// a listener to add feature, where we create a google.maps.polygon for every added feature, then add it to the polygon array
const addedPolygons = [];
addFeatureListener = map.data.addListener('addfeature', e => {
const featureGeometry = e.feature.getGeometry().getArray();
// for each geometry get the latLng array, which will be used to form a polygon
featureGeometry.forEach(latLngArray => {
const polygon = new window.google.maps.Polygon({
map,
paths: latLngArray.getArray(),
strokeWeight: 1,
fillColor: 'green',
clickable: false,
name: e.feature.getProperty('name')
});
addedPolygons.push(polygon);
});
setPolygonArray(addedPolygons);
});
// add the polygon to the map data layer (this will show the polygon on the map and cause the addfeature listener to fire)
map.data.addGeoJson(geofences);
map.data.setStyle({ fillColor: 'green', strokeWeight: 1 });
// we set map data to null so that we don't end up with 2 polygons on top of each other
map.data.setMap(null);
ref.current = 'geofenceEnabled';
}
} else {
ref.current = null;
for (const polygon of polygonArray) {
polygon.setMap(null);
}
setIsInfoWindowOpen(false);
}
};
getGeofences();
return () => {
if (addFeatureListener) {
addFeatureListener.remove();
}
};
}, [activeKmls]);
useEffect(() => {
let clickListener;
if (true) {
// a listener on click that checks whether the point is in a polygon and updates the necessary state to show the proper info window
clickListener = map.addListener('click', function(event) {
// this state is updated to identify the place where the info window will open
setGeofenceClickEventLatLng(event.latLng);
// for every polygon in the created polygons array check if it includes the clicked point, then update what to display in the info window
const selectedGeofences = [];
for (const polygon of polygonArray) {
if (window.google.maps.geometry.poly.containsLocation(event.latLng, polygon)) {
const name = polygon.name;
selectedGeofences.push({ key: name, name });
}
}
if (selectedGeofences.length) {
setIsInfoWindowOpen(true);
} else {
setIsInfoWindowOpen(false);
}
setGeofencesProperties(selectedGeofences);
});
}
return () => {
if (clickListener) {
clickListener.remove();
}
};
}, [activeKmls, polygonArray, altitudeUnit, map]);
return (
<Fragment>
{isInfoWindowOpen && (
<InfoWindow
position={geofenceClickEventLatLng}
onCloseClick={() => setIsInfoWindowOpen(false)}
zIndex={0}
options={{ maxWidth: '450' }}
>
<Fragment>
{geofencesProperties.map(geofenceProperties => {
return (
<Fragment key={geofenceProperties.key}>
<Title level={4}>{geofenceProperties.name}</Title>
</Fragment>
);
})}
</Fragment>
</InfoWindow>
)}
</Fragment>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>