From 7b8d1a81bf220b9fc4d949831bead0f51c05c0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Co=C5=9Fkun?= Date: Thu, 11 Jun 2020 12:45:05 +0300 Subject: [PATCH] nci-agency/anet#3045: Add all locations overlay to the map --- client/src/components/Leaflet.js | 97 +++++++++++++++++- .../resources/leaflet/marker-flag-blue.png | Bin 0 -> 2030 bytes .../src/resources/leaflet/marker-flag-red.png | Bin 0 -> 1240 bytes 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 client/src/resources/leaflet/marker-flag-blue.png create mode 100644 client/src/resources/leaflet/marker-flag-red.png diff --git a/client/src/components/Leaflet.js b/client/src/components/Leaflet.js index 85bf25ec61..619494dfe4 100644 --- a/client/src/components/Leaflet.js +++ b/client/src/components/Leaflet.js @@ -1,3 +1,5 @@ +import API from "api" +import { gql } from "apollo-boost" import { Control, CRS, Icon, Map, Marker, TileLayer } from "leaflet" import "leaflet-defaulticon-compatibility" import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css" @@ -14,13 +16,29 @@ import "leaflet.markercluster/dist/MarkerCluster.css" import "leaflet.markercluster/dist/MarkerCluster.Default.css" import "leaflet/dist/leaflet.css" import { Location } from "models" +import GeoLocation from "pages/locations/GeoLocation" import PropTypes from "prop-types" import React, { useCallback, useEffect, useRef, useState } from "react" +import ReactDOM from "react-dom" +import MARKER_FLAG_BLUE from "resources/leaflet/marker-flag-blue.png" import MARKER_ICON_2X from "resources/leaflet/marker-icon-2x.png" import MARKER_ICON from "resources/leaflet/marker-icon.png" import MARKER_SHADOW from "resources/leaflet/marker-shadow.png" import Settings from "settings" +const allLocationsQuery = gql` + query { + locationList(query: { pageSize: 0 }) { + list { + uuid + name + lat + lng + } + } + } +` + const css = { zIndex: 1 } @@ -68,6 +86,13 @@ const icon = new Icon({ shadowSize: [41, 41] }) +const icon2 = new Icon({ + iconUrl: MARKER_FLAG_BLUE, + iconSize: [64, 64], + iconAnchor: [32, 64], + popupAnchor: [4, -58] +}) + const addLayers = (map, layerControl) => { let defaultLayer = null Settings.imagery.baseLayers.forEach(layerConfig => { @@ -112,6 +137,8 @@ const Leaflet = ({ const [map, setMap] = useState(null) const [markerLayer, setMarkerLayer] = useState(null) + const [layerControl, setLayerControl] = useState(null) + const [allLocationsLayer, setAllLocationsLayer] = useState(null) const [doInitializeMarkerLayer, setDoInitializeMarkerLayer] = useState(false) const prevMarkersRef = useRef() @@ -125,7 +152,8 @@ const Leaflet = ({ icon: icon, draggable: m.draggable || false, autoPan: m.autoPan || false, - id: m.id + id: m.id, + zIndexOffset: 1000 }) if (m.name) { marker.bindPopup(m.name) @@ -170,6 +198,7 @@ const Leaflet = ({ const layerControl = new Control.Layers({}, {}, { collapsed: false }) layerControl.addTo(newMap) addLayers(newMap, layerControl) + setLayerControl(layerControl) setMap(newMap) @@ -242,6 +271,72 @@ const Leaflet = ({ widthPropUnchanged ]) + const { data } = API.useApiQuery(allLocationsQuery) + + if ( + data?.locationList?.list?.length && + map && + layerControl && + !allLocationsLayer + ) { + const allMarkers = data.locationList.list + .filter(loc => Location.hasCoordinates(loc)) + .map(location => { + const popupContent = document.createElement("div") + popupContent.setAttribute("style", "width: 300px;text-align: center") + + return new Marker([location.lat, location.lng], { + icon: icon2, + draggable: false, + autoPan: false, + id: location.uuid + }) + .bindPopup(popupContent) + .on("popupopen", e => { + // TODO LinkTo component will be utilized here to provide routing + ReactDOM.render( + <> + {location.name} @{" "} + + , + e.popup.getContent() + ) + }) + }) + + const locationsLayer = new MarkerClusterGroup() + .addLayers(allMarkers) + .on("add", () => { + if (markers?.length && markers.some(m => m.onMove)) { + return // While editing a location don't change zoom levels + } + + map.fitBounds(locationsLayer.getBounds()) + }) + .on("remove", () => { + if (markers?.length && markers.some(m => m.onMove)) { + return // While editing a location don't change zoom levels + } + + const curMarkersWithLatLng = (markers || []) + .filter(m => Location.hasCoordinates(m)) + .map(m => new Marker([m.lat, m.lng])) + + if (curMarkersWithLatLng.length) { + map.fitBounds( + new MarkerClusterGroup() + .addLayers(curMarkersWithLatLng) + .getBounds(), + { maxZoom: 15 } + ) + } + }) + + layerControl.addOverlay(locationsLayer, "All Locations") + + setAllLocationsLayer(locationsLayer) + } + return
} Leaflet.propTypes = { diff --git a/client/src/resources/leaflet/marker-flag-blue.png b/client/src/resources/leaflet/marker-flag-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..00dca0e1dfc101c05264f1a2cb6dec1c68ea11ca GIT binary patch literal 2030 zcmV|w#8 zEGo#NVo`)d6~r%zMJpjzRU`x@v7IQTiQ~pjJf4|%?_qK8n|V&giPPXbOU|*b?z>MO zzw`Z`?>YC!8^dFJYX)AWcjHHR{L%#5H)uQDoAf+o` z7gPilrxt~BokWQufN*W(tPUak&pZRN{n?P?s!ecg!6NhEXn$?u!{}S_+?Frll^FUO+{WR@8IR6GnAp zX=&x9-+cDi?;peu-3f5pF?SfHd@ZVI$g4tAMbuCw0}i_PnK zjDXr5Ma3on#nlt$4VII zeO~mm^FAfPJpoR_Av!TZ@<@u23w@tGy1#e+`PVN#wUr;;DFA0Mo(3%|z%=K)v?~2L z?`3M<$0)>oEK#0bMWaix|+`zrn8mw)&6p=o|}MnFnwk^+|30cvH%7xGlt ztN=}?rv*+HjwjZ4>g`eD5UbY)G z{VSJQAMm61hJ#CS?uoo4mb?>`vKthU@$qdAdOj*^s=udvo!rODNNLhMIVmOF5vyj7 z0~Z@VIwRol>gC0cKhfuJi+|_H;g9_CCoggP>dKAo2-x&aMr7+>+hOaB01-hXKfXKoof43jz~ezj)~Ny-0`{l^S_JG}1x(%y zb!FWV(7X*|kEVc`?d=pebLPz2o}LAOy`2KU-cAA7;|n3kueo4Pr@$Uw2f*VZn7aEdgEv_TWlr zKv$)2a>-kLeo;7yb~A32;^dc4-C0US0S`uil7aWg3O4Qr*&a9dQfo{z-W>#N|07M* zXV@J5X`tKS+kK=}D|Q6|+em@tB~cHUC!t(O60GL@L`F-T*!vy@0>GFRl>y&^-0V^vjdhGb|UW~B;-y?$@ zG65gA`a2I}ou)t?BO=0^Z@y72EiTRhbH%4f2Hh|Hj=etac0XG70?XI0&ygcvk--jt zb#@p$bWOnIBxsgNPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1Z_z~K~#8N?VDRj z6j2z*&+fRJ7c|L0Qmc7HCw4KE(+hu(%-(;Ba7;gtFRehTnicBYsy9a#HbMWo(ggHygK46O>G?q zdQR{Ap}NAaS|0qd;pYPtX6rb;FML+_MDaGWIKfhP<| zCLXXF;B&!%tD%S6)TS-02KZCVOfI-|qjSgO#+^&5YYT4)Xqwu{J#9Dd?|ycyYEpHR z!khul4BvX~Ze!QcN_l~i9ySA({n~H8=0FL^6~Fs<%~#b+3!4Ffs9xpEbq1t(yGsUZ z2^dC>FM!TnG}Knc@EQ=!`h3%37}@N1-oGeIt8RcO9$-NC(u0v%Gn7>*tww!?sUdcnxs61T4Jm z>X#jiN$WJ1 zoCwzM{nT=3M0NQLc<}sn>EY^%>Hg0HVrkd@y4#nV!|IT$u$4gR%AC*w0kaG$7Si6f zI|iEp1wMlw3X1~a-eQ;t<05FOK8GKTrGm`>+_%&706zJw2FStbsjycwfDG_x23QR6 zZU!jfo-{ztzNbq-(=>0FKy!2RjHllPfTv3Uc)A3@Rtb3dQW$vpIskZn5p?4#GU;GF zgKR#-O92uHfrp*6Z4Eo(`&kW$>aiz58?*r;Ai6*Xz6eEaKt@pYu}v-gE@?JfUIXZZ zk?b=8=S_kOEiHsoYiA0XK-t1ug!J)vmZgAmh9DA-fH>@tR;cLqxoTg z`7?E?F}IDH0wxd2*X!j7i7$R)N=IpnNhQHaRvK0I`(vsCAq)L_qLA z8MCUTn6tD9GRJBXjI(BhNnRK*KaX>alUVN%zaqmr%Mg<|)7Y07H2ZixmBt!a4ZwGV z@GvA2iI{1rX&Ti#R(gmB5I~|K1nFX1GaDfYm=yT@%krd;1lCLt|r!q+CW}Sy>rMAYK#hAjE%8Tx?X-{JfU{0000