diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 988f0b1a..4e4cdc4f 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@christopherpickering/react-leaflet-markercluster": "^1.1.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/base": "^5.0.0-beta.7",
@@ -1939,6 +1940,23 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@christopherpickering/react-leaflet-markercluster": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@christopherpickering/react-leaflet-markercluster/-/react-leaflet-markercluster-1.1.0.tgz",
+ "integrity": "sha512-69Q3c/Szq7vXNSq6wy+wi6Wj4yHHVxzAuJMiFMgTcoyuZO4EQj8a6qzOc/XdcuQbNX+gfFe2Me/C61bc6sjO4g==",
+ "dependencies": {
+ "@react-leaflet/core": "^2.1.0",
+ "leaflet": "^1.9.3",
+ "leaflet.markercluster": "^1.5.3",
+ "postcss-flexbugs-fixes": "^5.0.2",
+ "react-leaflet": "^4.0.0"
+ },
+ "peerDependencies": {
+ "leaflet": "^1.9.3",
+ "leaflet.markercluster": "^1.5.3",
+ "react-leaflet": "^4.0.0"
+ }
+ },
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -10094,6 +10112,14 @@
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
},
+ "node_modules/leaflet.markercluster": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz",
+ "integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==",
+ "peerDependencies": {
+ "leaflet": "^1.3.1"
+ }
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -10326,6 +10352,24 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
+ "node_modules/nanoid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "peer": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -13905,6 +13949,42 @@
"node": ">=10.4.0"
}
},
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-flexbugs-fixes": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz",
+ "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==",
+ "peerDependencies": {
+ "postcss": "^8.1.4"
+ }
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 8f05d3b8..eb32abe6 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -49,6 +49,7 @@
},
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@christopherpickering/react-leaflet-markercluster": "^1.1.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/base": "^5.0.0-beta.7",
diff --git a/frontend/src/components/Map/index.tsx b/frontend/src/components/Map/index.tsx
index d8669283..339dd644 100644
--- a/frontend/src/components/Map/index.tsx
+++ b/frontend/src/components/Map/index.tsx
@@ -4,10 +4,11 @@ import { MapContainer, GeoJSON, useMapEvents, TileLayer, Tooltip, Marker } from
import { useTheme, LinearProgress } from '@mui/material';
import { UseAppStoreType, AppContext } from '../../contexts/AppContext';
import { GeoJsonObject } from 'geojson';
-import { Icon, LeafletMouseEvent, Point } from 'leaflet';
+import { Icon, LeafletMouseEvent } from 'leaflet';
import { PublicOrder } from '../../models';
import OrderTooltip from '../Charts/helpers/OrderTooltip';
import getWorldmapGeojson from '../../geo/Web';
+import MarkerClusterGroup from '@christopherpickering/react-leaflet-markercluster';
interface Props {
orderType?: number;
@@ -91,14 +92,20 @@ const Map = ({
};
const getOrderMarkers = () => {
- return orders.map((order) => {
- if (!order.latitude || !order.longitude) return <>>;
- return RobotMarker(order.id, [order.latitude, order.longitude], order.type || 0, order);
- });
+ if (orders.length < 1) return <>>;
+ return (
+
+ {orders.map((order) => {
+ if (!order.latitude || !order.longitude) return <>>;
+ return RobotMarker(order.id, [order.latitude, order.longitude], order.type || 0, order);
+ })}
+
+ );
};
return (
svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
- pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: visiblePainted;
+ /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
@@ -276,9 +320,11 @@ svg.leaflet-image-layer.leaflet-interactive path {
background: #ddd;
outline-offset: 1px;
}
+
.leaflet-container a {
color: #0078a8;
}
+
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255, 255, 255, 0.5);
@@ -298,6 +344,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
border-radius: 4px;
}
+
.leaflet-bar a {
background-color: #fff;
border-bottom: 1px solid #ccc;
@@ -309,25 +356,30 @@ svg.leaflet-image-layer.leaflet-interactive path {
text-decoration: none;
color: black;
}
+
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
+
.leaflet-bar a:hover,
.leaflet-bar a:focus {
background-color: #f4f4f4;
}
+
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
+
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
+
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
@@ -339,10 +391,12 @@ svg.leaflet-image-layer.leaflet-interactive path {
height: 30px;
line-height: 30px;
}
+
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
+
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
@@ -371,47 +425,57 @@ svg.leaflet-image-layer.leaflet-interactive path {
background: #fff;
border-radius: 5px;
}
+
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
+
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
+
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
+
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
+
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
+
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
+
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
+
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
+
.leaflet-control-layers label {
display: block;
font-size: 13px;
font-size: 1.08333em;
}
+
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
@@ -431,31 +495,38 @@ svg.leaflet-image-layer.leaflet-interactive path {
background: rgba(255, 255, 255, 0.8);
margin: 0;
}
+
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
line-height: 1.4;
}
+
.leaflet-control-attribution a {
text-decoration: none;
}
+
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
+
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
+
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
+
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
+
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
@@ -467,11 +538,13 @@ svg.leaflet-image-layer.leaflet-interactive path {
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
+
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
+
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
@@ -481,6 +554,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
+
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0, 0, 0, 0.2);
@@ -494,11 +568,13 @@ svg.leaflet-image-layer.leaflet-interactive path {
text-align: center;
margin-bottom: 20px;
}
+
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
+
.leaflet-popup-content {
margin: 13px 24px 13px 20px;
line-height: 1.3;
@@ -506,10 +582,12 @@ svg.leaflet-image-layer.leaflet-interactive path {
font-size: 1.08333em;
min-height: 1px;
}
+
.leaflet-popup-content p {
margin: 17px 0;
margin: 1.3em 0;
}
+
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
@@ -520,6 +598,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
overflow: hidden;
pointer-events: none;
}
+
.leaflet-popup-tip {
width: 17px;
height: 17px;
@@ -533,12 +612,14 @@ svg.leaflet-image-layer.leaflet-interactive path {
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
+
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4);
}
+
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
@@ -555,10 +636,12 @@ svg.leaflet-image-layer.leaflet-interactive path {
text-decoration: none;
background: transparent;
}
+
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
color: #585858;
}
+
.leaflet-popup-scrolled {
overflow: auto;
}
@@ -566,6 +649,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
+
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
@@ -603,10 +687,12 @@ svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: none;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
+
.leaflet-tooltip.leaflet-interactive {
cursor: pointer;
pointer-events: auto;
}
+
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
@@ -623,41 +709,50 @@ svg.leaflet-image-layer.leaflet-interactive path {
.leaflet-tooltip-bottom {
margin-top: 6px;
}
+
.leaflet-tooltip-top {
margin-top: -6px;
}
+
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
+
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
+
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
+
.leaflet-tooltip-left {
margin-left: -6px;
}
+
.leaflet-tooltip-right {
margin-left: 6px;
}
+
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
+
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
+
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
@@ -673,3 +768,107 @@ svg.leaflet-image-layer.leaflet-interactive path {
print-color-adjust: exact;
}
}
+
+/* react-leaflet-cluster https://unpkg.com/@christopherpickering/react-leaflet-markercluster@1.1.0/dist/styles.min.css */
+
+.marker-cluster-small {
+ background-color: rgba(181, 226, 140, 0.6);
+}
+
+.marker-cluster-small div {
+ background-color: rgba(110, 204, 57, 0.6);
+}
+
+.marker-cluster-medium {
+ background-color: rgba(241, 211, 87, 0.6);
+}
+
+.marker-cluster-medium div {
+ background-color: rgba(240, 194, 12, 0.6);
+}
+
+.marker-cluster-large {
+ background-color: rgba(253, 156, 115, 0.6);
+}
+
+.marker-cluster-large div {
+ background-color: rgba(241, 128, 23, 0.6);
+}
+
+.leaflet-oldie .marker-cluster-small {
+ background-color: rgb(181, 226, 140);
+}
+
+.leaflet-oldie .marker-cluster-small div {
+ background-color: rgb(110, 204, 57);
+}
+
+.leaflet-oldie .marker-cluster-medium {
+ background-color: rgb(241, 211, 87);
+}
+
+.leaflet-oldie .marker-cluster-medium div {
+ background-color: rgb(240, 194, 12);
+}
+
+.leaflet-oldie .marker-cluster-large {
+ background-color: rgb(253, 156, 115);
+}
+
+.leaflet-oldie .marker-cluster-large div {
+ background-color: rgb(241, 128, 23);
+}
+
+.marker-cluster {
+ background-clip: padding-box;
+ border-radius: 20px;
+}
+
+.marker-cluster div {
+ width: 30px;
+ height: 30px;
+ margin-left: 5px;
+ margin-top: 5px;
+ text-align: center;
+ border-radius: 15px;
+ font:
+ 12px 'Helvetica Neue',
+ Arial,
+ Helvetica,
+ sans-serif;
+}
+
+.marker-cluster span {
+ line-height: 30px;
+}
+
+.leaflet-cluster-anim .leaflet-marker-icon,
+.leaflet-cluster-anim .leaflet-marker-shadow {
+ -webkit-transition:
+ -webkit-transform 0.3s ease-out,
+ opacity 0.3s ease-in;
+ -moz-transition:
+ -moz-transform 0.3s ease-out,
+ opacity 0.3s ease-in;
+ -o-transition:
+ -o-transform 0.3s ease-out,
+ opacity 0.3s ease-in;
+ transition:
+ transform 0.3s ease-out,
+ opacity 0.3s ease-in;
+}
+
+.leaflet-cluster-spider-leg {
+ -webkit-transition:
+ -webkit-stroke-dashoffset 0.3s ease-out,
+ -webkit-stroke-opacity 0.3s ease-in;
+ -moz-transition:
+ -moz-stroke-dashoffset 0.3s ease-out,
+ -moz-stroke-opacity 0.3s ease-in;
+ -o-transition:
+ -o-stroke-dashoffset 0.3s ease-out,
+ -o-stroke-opacity 0.3s ease-in;
+ transition:
+ stroke-dashoffset 0.3s ease-out,
+ stroke-opacity 0.3s ease-in;
+}