lml-main
Version:
This is now a mono repository published into many standalone packages.
332 lines • 12.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const React = require("react");
const cosmoui_1 = require("cosmoui");
const react_redux_1 = require("react-redux");
const constants_1 = require("../../mapping/constants");
const Actions = require("../../mapping/actions");
const Selectors = require("../../mapping/selectors");
const lodash_1 = require("lodash");
const Data = require("../../mapping/data");
// TODO - this result thingy needs to go
const Result = require("./result");
const ts_utils_1 = require("@lml/ts-utils");
const comparePoints = (a, b) => a && b && a.coordinates && a.coordinates &&
a.coordinates.latitude === b.coordinates.latitude
&& a.coordinates.longitude === b.coordinates.longitude;
class MapComponent extends React.Component {
constructor() {
super(...arguments);
this.mapCreated = false;
this.trafficLayer = null;
this.trafficLayerShowing = false;
this.markers = {};
this.polygons = {};
this.latLngJSONToPoint = (latLng) => ({
type: 'Point',
coordinates: {
latitude: latLng.lat,
longitude: latLng.lng,
},
});
this.pointToLatLng = (point) => new google.maps.LatLng(point.coordinates.latitude, point.coordinates.longitude);
this.setMapElRef = (mapEl) => this.mapEl = mapEl;
}
componentDidMount() {
this.createMapIfAPIAvailable();
}
componentDidUpdate() {
this.createMapIfAPIAvailable();
if (this.mapCreated) {
// console.log('MAP DID UPDATE', this.props.markers)
this.panToCenter();
this.showOrHideTrafficLayer();
this.updateMarkers();
this.updatePolygons();
this.updateDirections();
}
}
createMapIfAPIAvailable() {
if (this.props.google.mapsApiLoaded) {
this.createMap();
}
}
createMap() {
if (!this.mapCreated) {
const { lat, lng, zoom } = Data.defaultBounds;
const center = new google.maps.LatLng(lat, lng);
this.map = new google.maps.Map(this.mapEl, { center, zoom });
this.setUserLocation();
this.map.setOptions({
mapTypeControl: false,
streetViewControl: false,
styles: Data.mapStyle,
});
this.mapCreated = true;
this.addListenersToMap();
this.directionsService = new google.maps.DirectionsService();
this.directionsDisplay = new google.maps.DirectionsRenderer();
}
}
setUserLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
const { latitude, longitude } = position.coords;
const userLocation = {
type: 'Point',
coordinates: { latitude, longitude },
};
this.props.panMap(userLocation);
});
}
}
addListenersToMap() {
const boundsChanged = lodash_1.debounce(() => this.boundsChanged(), 1000);
this.map.addListener('bounds_changed', boundsChanged);
}
boundsChanged() {
const bounds = this.map.getBounds();
const center = this.map.getCenter().toJSON();
const ne = bounds.getNorthEast().toJSON();
const sw = bounds.getSouthWest().toJSON();
const nePt = this.latLngJSONToPoint(ne);
const swPt = this.latLngJSONToPoint(sw);
const centerPt = this.latLngJSONToPoint(center);
// console.log('BOUNDS CHANGED', this.center, centerPt)
if (!comparePoints(this.center, centerPt)) {
this.props.updateBounds(nePt, swPt, centerPt);
}
}
panToCenter() {
const { center } = this.props;
if (this.center !== center) {
this.map.panTo(this.pointToLatLng(center));
this.center = center;
}
}
showOrHideTrafficLayer() {
if (this.props.trafficShowing) {
this.showTrafficLayer();
return;
}
this.hideTrafficLayer();
}
showTrafficLayer() {
if (!this.trafficLayer) {
this.trafficLayer = new google.maps.TrafficLayer();
}
this.trafficLayer.setMap(this.map);
this.trafficLayerShowing = true;
}
hideTrafficLayer() {
if (this.trafficLayer && this.trafficLayerShowing) {
this.trafficLayer.setMap(null);
this.trafficLayerShowing = true; // really??? this seems improbable!
}
}
updateMarkers() {
constants_1.MAP_FILTER_NAMES.forEach((type) => {
this.updateMarkersForType(type, this.props.markers[type]);
});
}
updateMarkersForType(type, markerModels) {
if (!(this.markers[type] && Array.isArray(this.markers[type]))) {
this.markers[type] = [];
}
const mm = this.markersForTypeToMap(type);
markerModels.forEach((model) => {
if (model.position && model.position.coordinates) {
const marker = mm.get(model.id);
if (marker) {
this.updateMarker(marker, model);
mm.delete(model.id);
}
else {
this.addMarker(type, model);
}
}
else {
console.warn('model does not have proper coordinates', model);
}
});
this.removeMarkersInMapFromMap(type, mm);
}
updateMarker(marker, model) {
// console.log('UPDATE MARKER', marker, model)
marker.setPosition(this.pointToLatLng(model.position));
}
addMarker(type, model) {
const zIndex = ts_utils_1.getRandomIntOfLength(Data.icons()[type].zIndexLength || 4);
const config = {
position: this.pointToLatLng(model.position),
title: model.title,
icon: Object.assign({}, Data.icons()[type], { zIndex }),
zIndex,
map: this.map,
};
// only add labels to couriers
if (['courier', 'activeCourier'].indexOf(type) !== -1) {
config.label = {
text: model.label || '',
// fontWeight: 'bold',
fontSize: '10px',
fontFamily: 'Open Sans',
color: 'white',
};
}
// console.log('ADD MARKER', type, model, config)
const marker = new google.maps.Marker(config);
marker.id = model.id;
marker.addListener('click', () => {
this.props.markerClicked(marker.id);
});
this.markers[type].push(marker);
}
markersForTypeToMap(type) {
const mm = new Map();
this.markers[type].forEach((marker) => {
mm.set(marker.id, marker);
});
return mm;
}
removeMarkersInMapFromMap(type, mm) {
this.markers[type] = this.markers[type].filter((marker) => {
if (mm.has(marker.id)) {
// console.log('REMOVE MARKER', type, mm)
mm.get(marker.id).setMap(null);
return false;
}
return true;
});
}
updatePolygons() {
const { polygons } = this.props;
Object.keys(polygons).forEach((type) => {
this.updatePolygonsForType(type, polygons[type]);
});
}
updatePolygonsForType(type, polygons) {
if (!(this.polygons[type] && Array.isArray(this.polygons[type]))) {
this.polygons[type] = [];
}
const pm = this.polygonsForTypeToMap(type);
polygons.forEach((p) => {
if (pm.has(p.id)) {
// Already on the map so delete it from the Map
pm.delete(p.id);
}
else {
this.addPolygon(type, p);
}
});
this.removePolygonsInMapFromMap(type, pm);
}
polygonsForTypeToMap(type) {
const pm = new Map();
this.polygons[type].forEach((p) => {
pm.set(p.id, p);
});
return pm;
}
addPolygon(type, model) {
const options = Object.assign(Data.makeShapeForType(type), { paths: this.coordinatesToPath(model.perimeter) });
const polygon = new google.maps.Polygon(options);
polygon.id = model.id;
polygon.setMap(this.map);
this.polygons[type].push(polygon);
}
coordinatesToPath(perimeter) {
return perimeter.coordinates[0][0].map((point) => ({ lat: point[1], lng: point[0] }));
}
removePolygonsInMapFromMap(type, pm) {
this.polygons[type] = this.polygons[type].filter((p) => {
if (pm.has(p.id)) {
p.setMap(null);
return false;
}
return true;
});
}
updateDirections() {
const { directions } = this.props;
if (this.directions !== directions) {
// set the new directions on this component
this.directions = directions;
// it there are directions
if (directions) {
// then set them on the map
this.showDirections(directions);
}
else {
// otherwise remove the existing directions
this.directionsDisplay.setMap(null);
}
}
}
showDirections(directions) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const result = yield this.requestDirections(this.constructDirectionsRequest(directions));
if (result.success === true) {
this.directionsDisplay.setMap(this.map);
this.directionsDisplay.setDirections(result.value);
}
else {
console.error('Error displaying directions');
console.error(result.error);
console.error(result.error.stack);
}
});
}
requestDirections(request) {
return new Promise((resolve) => {
this.directionsService.route(request, (result, status) => {
if (status !== 'OK') {
return resolve(Result.mkFailure(status));
}
return resolve(Result.mkSuccess(result));
});
});
}
constructDirectionsRequest(directions) {
const dWaypoints = directions.waypoints.slice();
const origin = this.pointToLatLng(dWaypoints[0]);
const destination = this.pointToLatLng(dWaypoints.slice(-1)[0]);
const waypoints = dWaypoints.slice(1, -1).map((w) => ({ location: this.pointToLatLng(w), stopover: true }));
return {
origin,
destination,
waypoints,
travelMode: 'DRIVING',
transitOptions: {
departureTime: new Date(),
},
};
}
render() {
return (React.createElement(cosmoui_1.Column, { domRef: this.setMapElRef, onClick: this.props.onClick, className: this.props.className }, this.props.google.mapsAuthError
? 'The google maps key is invalid please contact support'
: 'Map loading...'));
}
}
const mapStateToProps = (state, ownProps) => ({
google: state.google,
trafficShowing: Selectors.getTrafficShowing(state),
center: Selectors.getMapCenter(state),
directions: Selectors.getMapDirections(state),
polygons: Selectors.getMapPolygons(state),
// todo - originally you could filter the markers
// according to which ones you wish to show or hide
// however this needs to be reimplemented using reselect
markers: Selectors.getMapMarkers(state),
});
const dispatchToProps = {
panMap: Actions.panMap,
showTraffic: Actions.showTraffic,
hideTraffic: Actions.hideTraffic,
updateBounds: Actions.updateBounds,
toggleMapFilter: Actions.toggleMapFilter,
markerClicked: Actions.markerClicked,
};
exports.Maps = react_redux_1.connect(mapStateToProps, dispatchToProps)(MapComponent);
//# sourceMappingURL=map.js.map