过滤谷歌地图自定义覆盖取决于他们是否落在当前的地图边界
Filter google maps custom overlays depending on whether they fall within the current map bounds
我正在努力寻找在react-redux项目中呈现自定义谷歌地图标记(覆盖)的适当方法。我有的只是一个显示地图和搜索框的页面。当有人搜索一个地方并且找到了这个地方时,这会触发map idle事件,然后我更新地图边界并搜索位置信息并将它们保存在redux存储中。然后取回带有当前地图边界和城市名称的数据。当数据到达时,过滤列表(过滤将在未来的后端,这意味着服务器将发送已经过滤的列表,落在当前视图中),并为地图上的每个列表呈现自定义覆盖。
在映射空闲事件:
1)更新地图边界和搜索的地名
从服务器获取一些数据(json格式)
3)过滤器列表,所以我们可以只渲染那些位置落在当前视口(地图边界)
4)为每个列表呈现自定义覆盖
对于每一个map idle事件,这似乎发生在你缩放或平移地图之后,整个过程的更新,提取,过滤和渲染重复。
到目前为止,我所做的是项目正在工作,直到React需要确定哪些覆盖应该被删除,哪些应该重新绘制。
实际上什么是不正确的工作现在是当可见的列表数组更新,React只是删除列表是在数组的末尾(从最后一个索引到0),而不是正确的一个(一个的位置是在视窗之外)。
有时候,如果你已经搜索了一个地方,并在地图上玩了一点,并试图搜索另一个地方,新的地方叠加没有正确定位。相反,它们离地图视窗很远。
我对所有的Ract, Redux和Google Maps Api技术都比较陌生,所以我意识到我可能做了一些非常愚蠢的事情。我希望这里有人能给我指出正确的方向。我搜遍了整个网络,也找不到一个合适的答案。我发现了一些关于如何创建自定义覆盖和如何为谷歌地图创建react组件的有用信息,我也知道有几个好的npm模块可以做我想要的工作(比如这个:react-google-maps和这个:google-map-react),但他们都有自己的问题,他们太复杂了我想要实现的。我很抱歉在这里粘贴了所有的代码,但我不确定如何在jsbin或类似的代码bin中表示整个项目环境。请让我知道,如果我需要使这样一个代码工作的例子,我会尝试。
这是我现在的代码。当然,这只是问题中重要的部分:
map组件
import React, { PropTypes, Component } from 'react';
import SearchBar from '../SearchBar';
import OverlayViewComponent from '../OverlayViewComponent';
import OverlayViewContent from '../OverlayViewContent';
import mapOptions from './cfg';
import MapStyles from './map.scss';
const propTypes = {
getListings: PropTypes.func.isRequired,
updateMapState: PropTypes.func.isRequired,
visibleListings: PropTypes.array.isRequired,
};
class GoogleMap extends Component {
constructor() {
super();
this._onMapIdle = this._onMapIdle.bind(this);
this.onPlacesSearch = this.onPlacesSearch.bind(this);
}
_initMap(mapContainer) {
// Create a new map
this._map = new google.maps.Map(mapContainer, mapOptions);
this._bindOnMapIdleEvent();
};
_bindOnMapIdleEvent() {
// Attach idle event listener to the map
this._map.addListener('idle', this._onMapIdle);
}
_onMapIdle() {
const { updateMapState, getListings } = this.props;
if (this._searchedPlace) {
console.log('ON MAP IDLE');
let mapBounds = this._map.getBounds().toJSON();
updateMapState(mapBounds, this._searchedPlace);
getListings();
}
};
onPlacesSearch(searchedPlace) {
if (searchedPlace.name !== '' && searchedPlace.geometry !== null) {
// Clear out the old marker if present.
if (this._searchedPlaceMarker) {
this._searchedPlaceMarker.setMap(null);
this._searchedPlaceMarker = null;
}
let bounds = new google.maps.LatLngBounds();
// Create a marker for the searched place.
this._searchedPlaceMarker = new google.maps.Marker({
map: this._map,
title: searchedPlace.name,
position: searchedPlace.geometry.location
});
if (searchedPlace.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(searchedPlace.geometry.viewport);
} else {
bounds.extend(searchedPlace.geometry.location);
}
// Save currently searchedPlace into the class local variable
this._searchedPlace = searchedPlace;
// Set map so it contains the searchedPlace marker (Ideally it should be only one)
this._map.fitBounds(bounds);
} else {
return;
}
}
componentDidMount() {
// When component is mounted, initialise the map
this._initMap(this._mapContainer);
};
shouldComponentUpdate(nextProps) {
if (nextProps.visibleListings.length == this.props.visibleListings.length) {
return false;
} else {
return true;
}
};
componentWillUnmount() {
google.maps.event.clearInstanceListeners(this._map);
};
render() {
console.log('GOOGLEMAP RENDER');
return (
<div id="mapContainer">
<div id="mapCanvas" ref={(mapContainer) => this._mapContainer = mapContainer}></div>
<SearchBar onPlacesSearch={this.onPlacesSearch} />
{
this.props.visibleListings.map((listing, index) => {
return (
<OverlayViewComponent key={index} mapInstance={this._map} position={listing.geo_tag}>
<OverlayViewContent listingData={listing} />
</OverlayViewComponent>
);
})
}
</div>
);
}
};
GoogleMap.propTypes = propTypes;
export default GoogleMap;
OverlayView组件
import React, { PropTypes, Component } from 'react';
import OverlayView from './utils/overlayViewHelpers';
const propTypes = {
position: PropTypes.array.isRequired,
mapInstance: PropTypes.object.isRequired,
};
class OverlayViewComponent extends Component {
componentDidMount() {
this._overlayInstance = new OverlayView(this.props.children, this.props.position, this.props.mapInstance);
};
componentWillUnmount() {
this._overlayInstance.setMap(null);
};
render() {
return null;
}
};
OverlayViewComponent.propTypes = propTypes;
export default OverlayViewComponent;
OverlayView类
import ReactDOM from 'react-dom';
const EL_WIDTH = 30;
const EL_HEIGHT = 35;
function OverlayView(element, position, map) {
this.overlayContent = element;
this.point = new google.maps.LatLng(position[0], position[1]);
this.setMap(map);
}
OverlayView.prototype = Object.create(new google.maps.OverlayView());
OverlayView.prototype.constructor = OverlayView;
OverlayView.prototype.onAdd = function() {
console.log('onAdd');
this.containerElement = document.createElement('div');
this.containerElement.style.position = 'absolute';
this.containerElement.style.width = EL_WIDTH + 'px';
this.containerElement.style.height = EL_HEIGHT + 'px';
let panes = this.getPanes();
panes.overlayMouseTarget.appendChild(this.containerElement);
ReactDOM.render(this.overlayContent, this.containerElement);
};
OverlayView.prototype.draw = function() {
console.log('draw');
if (this.containerElement) {
let projection = this.getProjection();
let projectedLatLng = projection.fromLatLngToDivPixel(this.point);
console.log(projectedLatLng);
this.containerElement.style.top = projectedLatLng.y - EL_HEIGHT + 'px';
this.containerElement.style.left = projectedLatLng.x - Math.floor(EL_WIDTH / 2) + 'px';
}
};
OverlayView.prototype.onRemove = function() {
console.log('onRemove');
let parentEl = this.containerElement.parentNode;
parentEl.removeChild(this.containerElement);
ReactDOM.unmountComponentAtNode(this.containerElement);
};
export default OverlayView;
OverlayView Content组件
import React, { PropTypes } from 'react';
import markerIcon from '../../../images/icon-marker.png';
const propTypes = {
listingData: PropTypes.object.isRequired,
};
const OverlayViewContent = (listingData) => {
console.log('OverlayViewContent render');
return (
<div className="customIcon">
<img src={markerIcon} title={listingData.where} />
</div>
);
};
OverlayViewContent.propTypes = propTypes;
export default OverlayViewContent;
这可能会对你有所帮助,尽管这个应用程序使用的是GMaps。
我想我已经找到问题的根源了。
感谢这篇文章,更具体地说,来自Sebastien Lorber的评论。他让我更彻底地看了一下react文档中关于键属性
的内容。如果我在遍历visblelistings数组时向react组件添加一个更独特的键,似乎一切都如预期的那样工作。问题是,我使用数组索引的组件键。这样,当visblelistings数组更新时,需要卸载的覆盖组件的键从数组中删除,react不知道应该卸载哪个组件。因此react总是从数组中删除最后一个覆盖组件。
- 将谷歌地图中的平移边界设置为图像覆盖
- 将边界设置为overlayImage谷歌地图
- 世界的最大纬度和长边界 - 谷歌地图API LatLngBounds().
- 谷歌地图API地理编码器,受边界限制
- 谷歌地图的多段线边界
- 在谷歌地图API中设置邮政编码边界
- 谷歌地图v3:你可以设置一个地图的边界
- 如何生成带有边界的谷歌地图
- 地图边界!如何检查地址是否位于边界中
- 谷歌地图路线拖动后的路线边界
- 谷歌地图边界不起作用
- 谷歌地图适合带填充的边界
- 如何为地图上的所有形状调整边界
- 仅在谷歌地图中显示国家边界
- 如何使用google.maps.Rectangle对象在谷歌地图上绘制选择/边界框/矩形,并检查标记是否落在其中
- 使用谷歌地图 API v3 的国家/地区边界
- 如何使地图适合传单中的切片图层边界
- 谷歌地图:地图不扩展边界以查看所有标记
- 谷歌地图绘制多边形并缩放到其边界
- 在不同大小的谷歌地图上设置边界