From bc23855777633dfd1caf7237b75c1e8fee88dff4 Mon Sep 17 00:00:00 2001
From: zaultavangar <zaul_tavangar@brown.edu>
Date: Mon, 11 Dec 2023 20:37:52 -0500
Subject: new updates to map feature

---
 src/client/views/nodes/MapBox/MarkerIcons.tsx | 87 +++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 src/client/views/nodes/MapBox/MarkerIcons.tsx

(limited to 'src/client/views/nodes/MapBox/MarkerIcons.tsx')

diff --git a/src/client/views/nodes/MapBox/MarkerIcons.tsx b/src/client/views/nodes/MapBox/MarkerIcons.tsx
new file mode 100644
index 000000000..cf50109ac
--- /dev/null
+++ b/src/client/views/nodes/MapBox/MarkerIcons.tsx
@@ -0,0 +1,87 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { faShopify } from '@fortawesome/free-brands-svg-icons';
+import { IconLookup, faBasketball, faBicycle, faBowlFood, faBus, faCameraRetro, faCar, faCartShopping, faFilm, faFootball, faFutbol, faHockeyPuck, faHospital, faHotel, faHouse, faLandmark, faMasksTheater, faMugSaucer, faPersonHiking, faPlane, faSchool, faShirt, faShop, faSquareParking, faStar, faTrainSubway, faTree, faUtensils, faVolleyball } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import React = require('react');
+
+export type MapboxColor = 'yellow' | 'red' | 'orange' | 'purple' | 'pink' | 'blue' | 'green';
+type ColorProperties = {
+    fill: string,
+    stroke: string
+}
+type ColorsMap = {
+    [key in MapboxColor]: ColorProperties;
+}
+
+export class MarkerIcons {
+
+    static getMapboxIcon = (color: string) => {
+        return (
+            <svg xmlns="http://www.w3.org/2000/svg" id="marker" data-name="marker" width="20" height="48" viewBox="0 0 20 35">
+            <g id="mapbox-marker-icon">
+                <g id="icon">
+                <ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" />
+                <g id="mask" opacity="0.3">
+                    <g id="group">
+                    <path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fillRule="evenodd"/>
+                    </g>
+                </g>
+                <path id="color" fill={color} strokeWidth="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
+                <path id="circle" fill="#fff" stroke='white' strokeWidth="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
+                </g>
+            </g>
+            <rect width="20" height="48" fill="none"/>
+        </svg>
+        )
+    }
+
+    static getFontAwesomeIcon(key: string, color?: string): JSX.Element | undefined {
+        const icon: IconProp = MarkerIcons.FAMarkerIconsMap[key];
+
+        if (icon) {
+            const iconProps: any = { icon };
+        
+            if (color) {
+              iconProps.color = color;
+            }
+        
+            return (<FontAwesomeIcon {...iconProps} size='1x' />);
+          }
+        
+        return undefined;
+    }
+
+    static FAMarkerIconsMap: {[key: string]: IconProp} = {
+        'RESTAURANT_ICON': faUtensils,
+        'HOTEL_ICON': faHotel,
+        'HOUSE_ICON': faHouse,
+        'AIRPLANE_ICON': faPlane,
+        'CAR_ICON': faCar,
+        'BUS_ICON': faBus,
+        'TRAIN_ICON': faTrainSubway,
+        'BICYCLE_ICON': faBicycle,
+        'PARKING_ICON': faSquareParking,
+        'PHOTO_ICON': faCameraRetro,
+        'CAFE_ICON': faMugSaucer,
+        'STAR_ICON': faStar,
+        'SHOPPING_CART_ICON': faCartShopping,
+        'SHOPIFY_ICON': faShopify,
+        'SHOP_ICON': faShop,
+        'SHIRT_ICON': faShirt,
+        'FOOD_ICON': faBowlFood,
+        'LANDMARK_ICON': faLandmark,
+        'HOSPITAL_ICON': faHospital,
+        'NATURE_ICON': faTree,
+        'HIKING_ICON': faPersonHiking,
+        'SOCCER_ICON': faFutbol,
+        'VOLLEYBALL_ICON': faVolleyball,
+        'BASKETBALL_ICON': faBasketball,
+        'HOCKEY_ICON': faHockeyPuck,
+        'FOOTBALL_ICON': faFootball,
+        'SCHOOL_ICON': faSchool,
+        'THEATER_ICON': faMasksTheater,
+        'FILM_ICON': faFilm
+    }
+
+
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From e9b59a2790006af60206af7f676ad4a0ccceee13 Mon Sep 17 00:00:00 2001
From: zaultavangar <zaul_tavangar@brown.edu>
Date: Thu, 14 Dec 2023 12:43:40 -0500
Subject: adding animation functionality

---
 package-lock.json                                  | 1433 +++++++++++++++++++-
 package.json                                       |    9 +-
 .../views/nodes/MapBox/AnimationSpeedIcons.tsx     |   35 +
 src/client/views/nodes/MapBox/AnimationUtility.ts  |  429 ++++++
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx    |  118 +-
 src/client/views/nodes/MapBox/MapBox.scss          |   50 +
 src/client/views/nodes/MapBox/MapBox.tsx           |  776 +++++++++--
 src/client/views/nodes/MapBox/MarkerIcons.tsx      |   69 +-
 8 files changed, 2714 insertions(+), 205 deletions(-)
 create mode 100644 src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx
 create mode 100644 src/client/views/nodes/MapBox/AnimationUtility.ts

(limited to 'src/client/views/nodes/MapBox/MarkerIcons.tsx')

diff --git a/package-lock.json b/package-lock.json
index 58b3d9eae..311eb9281 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5329,6 +5329,1294 @@
                 }
             }
         },
+        "@turf/along": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.5.0.tgz",
+            "integrity": "sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw==",
+            "requires": {
+                "@turf/bearing": "^6.5.0",
+                "@turf/destination": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/angle": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-6.5.0.tgz",
+            "integrity": "sha512-4pXMbWhFofJJAOvTMCns6N4C8CMd5Ih4O2jSAG9b3dDHakj3O4yN1+Zbm+NUei+eVEZ9gFeVp9svE3aMDenIkw==",
+            "requires": {
+                "@turf/bearing": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/rhumb-bearing": "^6.5.0"
+            }
+        },
+        "@turf/area": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.5.0.tgz",
+            "integrity": "sha512-xCZdiuojokLbQ+29qR6qoMD89hv+JAgWjLrwSEWL+3JV8IXKeNFl6XkEJz9HGkVpnXvQKJoRz4/liT+8ZZ5Jyg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/bbox": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz",
+            "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/bbox-clip": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-6.5.0.tgz",
+            "integrity": "sha512-F6PaIRF8WMp8EmgU/Ke5B1Y6/pia14UAYB5TiBC668w5rVVjy5L8rTm/m2lEkkDMHlzoP9vNY4pxpNthE7rLcQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/bbox-polygon": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-6.5.0.tgz",
+            "integrity": "sha512-+/r0NyL1lOG3zKZmmf6L8ommU07HliP4dgYToMoTxqzsWzyLjaj/OzgQ8rBmv703WJX+aS6yCmLuIhYqyufyuw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/bearing": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.5.0.tgz",
+            "integrity": "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/bezier-spline": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-6.5.0.tgz",
+            "integrity": "sha512-vokPaurTd4PF96rRgGVm6zYYC5r1u98ZsG+wZEv9y3kJTuJRX/O3xIY2QnTGTdbVmAJN1ouOsD0RoZYaVoXORQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/boolean-clockwise": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-6.5.0.tgz",
+            "integrity": "sha512-45+C7LC5RMbRWrxh3Z0Eihsc8db1VGBO5d9BLTOAwU4jR6SgsunTfRWR16X7JUwIDYlCVEmnjcXJNi/kIU3VIw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/boolean-contains": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-6.5.0.tgz",
+            "integrity": "sha512-4m8cJpbw+YQcKVGi8y0cHhBUnYT+QRfx6wzM4GI1IdtYH3p4oh/DOBJKrepQyiDzFDaNIjxuWXBh0ai1zVwOQQ==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/boolean-point-on-line": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/boolean-crosses": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-6.5.0.tgz",
+            "integrity": "sha512-gvshbTPhAHporTlQwBJqyfW+2yV8q/mOTxG6PzRVl6ARsqNoqYQWkd4MLug7OmAqVyBzLK3201uAeBjxbGw0Ng==",
+            "requires": {
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-intersect": "^6.5.0",
+                "@turf/polygon-to-line": "^6.5.0"
+            }
+        },
+        "@turf/boolean-disjoint": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-6.5.0.tgz",
+            "integrity": "sha512-rZ2ozlrRLIAGo2bjQ/ZUu4oZ/+ZjGvLkN5CKXSKBcu6xFO6k2bgqeM8a1836tAW+Pqp/ZFsTA5fZHsJZvP2D5g==",
+            "requires": {
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/line-intersect": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/polygon-to-line": "^6.5.0"
+            }
+        },
+        "@turf/boolean-equal": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-6.5.0.tgz",
+            "integrity": "sha512-cY0M3yoLC26mhAnjv1gyYNQjn7wxIXmL2hBmI/qs8g5uKuC2hRWi13ydufE3k4x0aNRjFGlg41fjoYLwaVF+9Q==",
+            "requires": {
+                "@turf/clean-coords": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "geojson-equality": "0.1.6"
+            }
+        },
+        "@turf/boolean-intersects": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-6.5.0.tgz",
+            "integrity": "sha512-nIxkizjRdjKCYFQMnml6cjPsDOBCThrt+nkqtSEcxkKMhAQj5OO7o2CecioNTaX8EayqwMGVKcsz27oP4mKPTw==",
+            "requires": {
+                "@turf/boolean-disjoint": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/boolean-overlap": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-6.5.0.tgz",
+            "integrity": "sha512-8btMIdnbXVWUa1M7D4shyaSGxLRw6NjMcqKBcsTXcZdnaixl22k7ar7BvIzkaRYN3SFECk9VGXfLncNS3ckQUw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-intersect": "^6.5.0",
+                "@turf/line-overlap": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "geojson-equality": "0.1.6"
+            }
+        },
+        "@turf/boolean-parallel": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-6.5.0.tgz",
+            "integrity": "sha512-aSHJsr1nq9e5TthZGZ9CZYeXklJyRgR5kCLm5X4urz7+MotMOp/LsGOsvKvK9NeUl9+8OUmfMn8EFTT8LkcvIQ==",
+            "requires": {
+                "@turf/clean-coords": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/line-segment": "^6.5.0",
+                "@turf/rhumb-bearing": "^6.5.0"
+            }
+        },
+        "@turf/boolean-point-in-polygon": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz",
+            "integrity": "sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/boolean-point-on-line": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-6.5.0.tgz",
+            "integrity": "sha512-A1BbuQ0LceLHvq7F/P7w3QvfpmZqbmViIUPHdNLvZimFNLo4e6IQunmzbe+8aSStH9QRZm3VOflyvNeXvvpZEQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/boolean-within": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-6.5.0.tgz",
+            "integrity": "sha512-YQB3oU18Inx35C/LU930D36RAVe7LDXk1kWsQ8mLmuqYn9YdPsDQTMTkLJMhoQ8EbN7QTdy333xRQ4MYgToteQ==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/boolean-point-on-line": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/buffer": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-6.5.0.tgz",
+            "integrity": "sha512-qeX4N6+PPWbKqp1AVkBVWFerGjMYMUyencwfnkCesoznU6qvfugFHNAngNqIBVnJjZ5n8IFyOf+akcxnrt9sNg==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/center": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/projection": "^6.5.0",
+                "d3-geo": "1.7.1",
+                "turf-jsts": "*"
+            },
+            "dependencies": {
+                "d3-array": {
+                    "version": "1.2.4",
+                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+                    "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+                },
+                "d3-geo": {
+                    "version": "1.7.1",
+                    "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz",
+                    "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==",
+                    "requires": {
+                        "d3-array": "1"
+                    }
+                }
+            }
+        },
+        "@turf/center": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/center/-/center-6.5.0.tgz",
+            "integrity": "sha512-T8KtMTfSATWcAX088rEDKjyvQCBkUsLnK/Txb6/8WUXIeOZyHu42G7MkdkHRoHtwieLdduDdmPLFyTdG5/e7ZQ==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/center-mean": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-6.5.0.tgz",
+            "integrity": "sha512-AAX6f4bVn12pTVrMUiB9KrnV94BgeBKpyg3YpfnEbBpkN/znfVhL8dG8IxMAxAoSZ61Zt9WLY34HfENveuOZ7Q==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/center-median": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-6.5.0.tgz",
+            "integrity": "sha512-dT8Ndu5CiZkPrj15PBvslpuf01ky41DEYEPxS01LOxp5HOUHXp1oJxsPxvc+i/wK4BwccPNzU1vzJ0S4emd1KQ==",
+            "requires": {
+                "@turf/center-mean": "^6.5.0",
+                "@turf/centroid": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/center-of-mass": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-6.5.0.tgz",
+            "integrity": "sha512-EWrriU6LraOfPN7m1jZi+1NLTKNkuIsGLZc2+Y8zbGruvUW+QV7K0nhf7iZWutlxHXTBqEXHbKue/o79IumAsQ==",
+            "requires": {
+                "@turf/centroid": "^6.5.0",
+                "@turf/convex": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/centroid": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.5.0.tgz",
+            "integrity": "sha512-MwE1oq5E3isewPprEClbfU5pXljIK/GUOMbn22UM3IFPDJX0KeoyLNwghszkdmFp/qMGL/M13MMWvU+GNLXP/A==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/circle": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-6.5.0.tgz",
+            "integrity": "sha512-oU1+Kq9DgRnoSbWFHKnnUdTmtcRUMmHoV9DjTXu9vOLNV5OWtAAh1VZ+mzsioGGzoDNT/V5igbFOkMfBQc0B6A==",
+            "requires": {
+                "@turf/destination": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/clean-coords": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-6.5.0.tgz",
+            "integrity": "sha512-EMX7gyZz0WTH/ET7xV8MyrExywfm9qUi0/MY89yNffzGIEHuFfqwhcCqZ8O00rZIPZHUTxpmsxQSTfzJJA1CPw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/clone": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz",
+            "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/clusters": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-6.5.0.tgz",
+            "integrity": "sha512-Y6gfnTJzQ1hdLfCsyd5zApNbfLIxYEpmDibHUqR5z03Lpe02pa78JtgrgUNt1seeO/aJ4TG1NLN8V5gOrHk04g==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/clusters-dbscan": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.5.0.tgz",
+            "integrity": "sha512-SxZEE4kADU9DqLRiT53QZBBhu8EP9skviSyl+FGj08Y01xfICM/RR9ACUdM0aEQimhpu+ZpRVcUK+2jtiCGrYQ==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "density-clustering": "1.3.0"
+            }
+        },
+        "@turf/clusters-kmeans": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.5.0.tgz",
+            "integrity": "sha512-DwacD5+YO8kwDPKaXwT9DV46tMBVNsbi1IzdajZu1JDSWoN7yc7N9Qt88oi+p30583O0UPVkAK+A10WAQv4mUw==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "skmeans": "0.9.7"
+            }
+        },
+        "@turf/collect": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-6.5.0.tgz",
+            "integrity": "sha512-4dN/T6LNnRg099m97BJeOcTA5fSI8cu87Ydgfibewd2KQwBexO69AnjEFqfPX3Wj+Zvisj1uAVIZbPmSSrZkjg==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "rbush": "2.x"
+            },
+            "dependencies": {
+                "quickselect": {
+                    "version": "1.1.1",
+                    "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz",
+                    "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ=="
+                },
+                "rbush": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz",
+                    "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==",
+                    "requires": {
+                        "quickselect": "^1.0.1"
+                    }
+                }
+            }
+        },
+        "@turf/combine": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-6.5.0.tgz",
+            "integrity": "sha512-Q8EIC4OtAcHiJB3C4R+FpB4LANiT90t17uOd851qkM2/o6m39bfN5Mv0PWqMZIHWrrosZqRqoY9dJnzz/rJxYQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/concave": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-6.5.0.tgz",
+            "integrity": "sha512-I/sUmUC8TC5h/E2vPwxVht+nRt+TnXIPRoztDFvS8/Y0+cBDple9inLSo9nnPXMXidrBlGXZ9vQx/BjZUJgsRQ==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/tin": "^6.5.0",
+                "topojson-client": "3.x",
+                "topojson-server": "3.x"
+            }
+        },
+        "@turf/convex": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-6.5.0.tgz",
+            "integrity": "sha512-x7ZwC5z7PJB0SBwNh7JCeCNx7Iu+QSrH7fYgK0RhhNop13TqUlvHMirMLRgf2db1DqUetrAO2qHJeIuasquUWg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "concaveman": "*"
+            }
+        },
+        "@turf/destination": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.5.0.tgz",
+            "integrity": "sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/difference": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-6.5.0.tgz",
+            "integrity": "sha512-l8iR5uJqvI+5Fs6leNbhPY5t/a3vipUF/3AeVLpwPQcgmedNXyheYuy07PcMGH5Jdpi5gItOiTqwiU/bUH4b3A==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "polygon-clipping": "^0.15.3"
+            }
+        },
+        "@turf/dissolve": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-6.5.0.tgz",
+            "integrity": "sha512-WBVbpm9zLTp0Bl9CE35NomTaOL1c4TQCtEoO43YaAhNEWJOOIhZMFJyr8mbvYruKl817KinT3x7aYjjCMjTAsQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "polygon-clipping": "^0.15.3"
+            }
+        },
+        "@turf/distance": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz",
+            "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/distance-weight": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-6.5.0.tgz",
+            "integrity": "sha512-a8qBKkgVNvPKBfZfEJZnC3DV7dfIsC3UIdpRci/iap/wZLH41EmS90nM+BokAJflUHYy8PqE44wySGWHN1FXrQ==",
+            "requires": {
+                "@turf/centroid": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/ellipse": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-6.5.0.tgz",
+            "integrity": "sha512-kuXtwFviw/JqnyJXF1mrR/cb496zDTSbGKtSiolWMNImYzGGkbsAsFTjwJYgD7+4FixHjp0uQPzo70KDf3AIBw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/rhumb-destination": "^6.5.0",
+                "@turf/transform-rotate": "^6.5.0"
+            }
+        },
+        "@turf/envelope": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-6.5.0.tgz",
+            "integrity": "sha512-9Z+FnBWvOGOU4X+fMZxYFs1HjFlkKqsddLuMknRaqcJd6t+NIv5DWvPtDL8ATD2GEExYDiFLwMdckfr1yqJgHA==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/bbox-polygon": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/explode": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-6.5.0.tgz",
+            "integrity": "sha512-6cSvMrnHm2qAsace6pw9cDmK2buAlw8+tjeJVXMfMyY+w7ZUi1rprWMsY92J7s2Dar63Bv09n56/1V7+tcj52Q==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/flatten": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-6.5.0.tgz",
+            "integrity": "sha512-IBZVwoNLVNT6U/bcUUllubgElzpMsNoCw8tLqBw6dfYg9ObGmpEjf9BIYLr7a2Yn5ZR4l7YIj2T7kD5uJjZADQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/flip": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-6.5.0.tgz",
+            "integrity": "sha512-oyikJFNjt2LmIXQqgOGLvt70RgE2lyzPMloYWM7OR5oIFGRiBvqVD2hA6MNw6JewIm30fWZ8DQJw1NHXJTJPbg==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/great-circle": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-6.5.0.tgz",
+            "integrity": "sha512-7ovyi3HaKOXdFyN7yy1yOMa8IyOvV46RC1QOQTT+RYUN8ke10eyqExwBpL9RFUPvlpoTzoYbM/+lWPogQlFncg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/helpers": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
+            "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="
+        },
+        "@turf/hex-grid": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-6.5.0.tgz",
+            "integrity": "sha512-Ln3tc2tgZT8etDOldgc6e741Smg1CsMKAz1/Mlel+MEL5Ynv2mhx3m0q4J9IB1F3a4MNjDeVvm8drAaf9SF33g==",
+            "requires": {
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/intersect": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/interpolate": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-6.5.0.tgz",
+            "integrity": "sha512-LSH5fMeiGyuDZ4WrDJNgh81d2DnNDUVJtuFryJFup8PV8jbs46lQGfI3r1DJ2p1IlEJIz3pmAZYeTfMMoeeohw==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/centroid": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/hex-grid": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/point-grid": "^6.5.0",
+                "@turf/square-grid": "^6.5.0",
+                "@turf/triangle-grid": "^6.5.0"
+            }
+        },
+        "@turf/intersect": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.5.0.tgz",
+            "integrity": "sha512-2legGJeKrfFkzntcd4GouPugoqPUjexPZnOvfez+3SfIMrHvulw8qV8u7pfVyn2Yqs53yoVCEjS5sEpvQ5YRQg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "polygon-clipping": "^0.15.3"
+            }
+        },
+        "@turf/invariant": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
+            "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/isobands": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-6.5.0.tgz",
+            "integrity": "sha512-4h6sjBPhRwMVuFaVBv70YB7eGz+iw0bhPRnp+8JBdX1UPJSXhoi/ZF2rACemRUr0HkdVB/a1r9gC32vn5IAEkw==",
+            "requires": {
+                "@turf/area": "^6.5.0",
+                "@turf/bbox": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/explode": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "object-assign": "*"
+            }
+        },
+        "@turf/isolines": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-6.5.0.tgz",
+            "integrity": "sha512-6ElhiLCopxWlv4tPoxiCzASWt/jMRvmp6mRYrpzOm3EUl75OhHKa/Pu6Y9nWtCMmVC/RcWtiiweUocbPLZLm0A==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "object-assign": "*"
+            }
+        },
+        "@turf/kinks": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-6.5.0.tgz",
+            "integrity": "sha512-ViCngdPt1eEL7hYUHR2eHR662GvCgTc35ZJFaNR6kRtr6D8plLaDju0FILeFFWSc+o8e3fwxZEJKmFj9IzPiIQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/length": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/length/-/length-6.5.0.tgz",
+            "integrity": "sha512-5pL5/pnw52fck3oRsHDcSGrj9HibvtlrZ0QNy2OcW8qBFDNgZ4jtl6U7eATVoyWPKBHszW3dWETW+iLV7UARig==",
+            "requires": {
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/line-arc": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-6.5.0.tgz",
+            "integrity": "sha512-I6c+V6mIyEwbtg9P9zSFF89T7QPe1DPTG3MJJ6Cm1MrAY0MdejwQKOpsvNl8LDU2ekHOlz2kHpPVR7VJsoMllA==",
+            "requires": {
+                "@turf/circle": "^6.5.0",
+                "@turf/destination": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/line-chunk": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-6.5.0.tgz",
+            "integrity": "sha512-i1FGE6YJaaYa+IJesTfyRRQZP31QouS+wh/pa6O3CC0q4T7LtHigyBSYjrbjSLfn2EVPYGlPCMFEqNWCOkC6zg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/length": "^6.5.0",
+                "@turf/line-slice-along": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/line-intersect": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.5.0.tgz",
+            "integrity": "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-segment": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "geojson-rbush": "3.x"
+            }
+        },
+        "@turf/line-offset": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-6.5.0.tgz",
+            "integrity": "sha512-CEXZbKgyz8r72qRvPchK0dxqsq8IQBdH275FE6o4MrBkzMcoZsfSjghtXzKaz9vvro+HfIXal0sTk2mqV1lQTw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/line-overlap": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-6.5.0.tgz",
+            "integrity": "sha512-xHOaWLd0hkaC/1OLcStCpfq55lPHpPNadZySDXYiYjEz5HXr1oKmtMYpn0wGizsLwrOixRdEp+j7bL8dPt4ojQ==",
+            "requires": {
+                "@turf/boolean-point-on-line": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-segment": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/nearest-point-on-line": "^6.5.0",
+                "deep-equal": "1.x",
+                "geojson-rbush": "3.x"
+            }
+        },
+        "@turf/line-segment": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.5.0.tgz",
+            "integrity": "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/line-slice": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-6.5.0.tgz",
+            "integrity": "sha512-vDqJxve9tBHhOaVVFXqVjF5qDzGtKWviyjbyi2QnSnxyFAmLlLnBfMX8TLQCAf2GxHibB95RO5FBE6I2KVPRuw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/nearest-point-on-line": "^6.5.0"
+            }
+        },
+        "@turf/line-slice-along": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-6.5.0.tgz",
+            "integrity": "sha512-KHJRU6KpHrAj+BTgTNqby6VCTnDzG6a1sJx/I3hNvqMBLvWVA2IrkR9L9DtsQsVY63IBwVdQDqiwCuZLDQh4Ng==",
+            "requires": {
+                "@turf/bearing": "^6.5.0",
+                "@turf/destination": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/line-split": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-6.5.0.tgz",
+            "integrity": "sha512-/rwUMVr9OI2ccJjw7/6eTN53URtGThNSD5I0GgxyFXMtxWiloRJ9MTff8jBbtPWrRka/Sh2GkwucVRAEakx9Sw==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-intersect": "^6.5.0",
+                "@turf/line-segment": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/nearest-point-on-line": "^6.5.0",
+                "@turf/square": "^6.5.0",
+                "@turf/truncate": "^6.5.0",
+                "geojson-rbush": "3.x"
+            }
+        },
+        "@turf/line-to-polygon": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-6.5.0.tgz",
+            "integrity": "sha512-qYBuRCJJL8Gx27OwCD1TMijM/9XjRgXH/m/TyuND4OXedBpIWlK5VbTIO2gJ8OCfznBBddpjiObLBrkuxTpN4Q==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/mask": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-6.5.0.tgz",
+            "integrity": "sha512-RQha4aU8LpBrmrkH8CPaaoAfk0Egj5OuXtv6HuCQnHeGNOQt3TQVibTA3Sh4iduq4EPxnZfDjgsOeKtrCA19lg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "polygon-clipping": "^0.15.3"
+            }
+        },
+        "@turf/meta": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
+            "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/midpoint": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-6.5.0.tgz",
+            "integrity": "sha512-MyTzV44IwmVI6ec9fB2OgZ53JGNlgOpaYl9ArKoF49rXpL84F9rNATndbe0+MQIhdkw8IlzA6xVP4lZzfMNVCw==",
+            "requires": {
+                "@turf/bearing": "^6.5.0",
+                "@turf/destination": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/moran-index": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-6.5.0.tgz",
+            "integrity": "sha512-ItsnhrU2XYtTtTudrM8so4afBCYWNaB0Mfy28NZwLjB5jWuAsvyV+YW+J88+neK/ougKMTawkmjQqodNJaBeLQ==",
+            "requires": {
+                "@turf/distance-weight": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/nearest-point": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-6.5.0.tgz",
+            "integrity": "sha512-fguV09QxilZv/p94s8SMsXILIAMiaXI5PATq9d7YWijLxWUj6Q/r43kxyoi78Zmwwh1Zfqz9w+bCYUAxZ5+euA==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/nearest-point-on-line": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz",
+            "integrity": "sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==",
+            "requires": {
+                "@turf/bearing": "^6.5.0",
+                "@turf/destination": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-intersect": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/nearest-point-to-line": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-6.5.0.tgz",
+            "integrity": "sha512-PXV7cN0BVzUZdjj6oeb/ESnzXSfWmEMrsfZSDRgqyZ9ytdiIj/eRsnOXLR13LkTdXVOJYDBuf7xt1mLhM4p6+Q==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/point-to-line-distance": "^6.5.0",
+                "object-assign": "*"
+            }
+        },
+        "@turf/planepoint": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-6.5.0.tgz",
+            "integrity": "sha512-R3AahA6DUvtFbka1kcJHqZ7DMHmPXDEQpbU5WaglNn7NaCQg9HB0XM0ZfqWcd5u92YXV+Gg8QhC8x5XojfcM4Q==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/point-grid": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-6.5.0.tgz",
+            "integrity": "sha512-Iq38lFokNNtQJnOj/RBKmyt6dlof0yhaHEDELaWHuECm1lIZLY3ZbVMwbs+nXkwTAHjKfS/OtMheUBkw+ee49w==",
+            "requires": {
+                "@turf/boolean-within": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/point-on-feature": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-6.5.0.tgz",
+            "integrity": "sha512-bDpuIlvugJhfcF/0awAQ+QI6Om1Y1FFYE8Y/YdxGRongivix850dTeXCo0mDylFdWFPGDo7Mmh9Vo4VxNwW/TA==",
+            "requires": {
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/center": "^6.5.0",
+                "@turf/explode": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/nearest-point": "^6.5.0"
+            }
+        },
+        "@turf/point-to-line-distance": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-6.5.0.tgz",
+            "integrity": "sha512-opHVQ4vjUhNBly1bob6RWy+F+hsZDH9SA0UW36pIRzfpu27qipU18xup0XXEePfY6+wvhF6yL/WgCO2IbrLqEA==",
+            "requires": {
+                "@turf/bearing": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/projection": "^6.5.0",
+                "@turf/rhumb-bearing": "^6.5.0",
+                "@turf/rhumb-distance": "^6.5.0"
+            }
+        },
+        "@turf/points-within-polygon": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-6.5.0.tgz",
+            "integrity": "sha512-YyuheKqjliDsBDt3Ho73QVZk1VXX1+zIA2gwWvuz8bR1HXOkcuwk/1J76HuFMOQI3WK78wyAi+xbkx268PkQzQ==",
+            "requires": {
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/polygon-smooth": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-6.5.0.tgz",
+            "integrity": "sha512-LO/X/5hfh/Rk4EfkDBpLlVwt3i6IXdtQccDT9rMjXEP32tRgy0VMFmdkNaXoGlSSKf/1mGqLl4y4wHd86DqKbg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/polygon-tangents": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-6.5.0.tgz",
+            "integrity": "sha512-sB4/IUqJMYRQH9jVBwqS/XDitkEfbyqRy+EH/cMRJURTg78eHunvJ708x5r6umXsbiUyQU4eqgPzEylWEQiunw==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/boolean-within": "^6.5.0",
+                "@turf/explode": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/nearest-point": "^6.5.0"
+            }
+        },
+        "@turf/polygon-to-line": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-6.5.0.tgz",
+            "integrity": "sha512-5p4n/ij97EIttAq+ewSnKt0ruvuM+LIDzuczSzuHTpq4oS7Oq8yqg5TQ4nzMVuK41r/tALCk7nAoBuw3Su4Gcw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/polygonize": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-6.5.0.tgz",
+            "integrity": "sha512-a/3GzHRaCyzg7tVYHo43QUChCspa99oK4yPqooVIwTC61npFzdrmnywMv0S+WZjHZwK37BrFJGFrZGf6ocmY5w==",
+            "requires": {
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/envelope": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/projection": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.5.0.tgz",
+            "integrity": "sha512-/Pgh9mDvQWWu8HRxqpM+tKz8OzgauV+DiOcr3FCjD6ubDnrrmMJlsf6fFJmggw93mtVPrZRL6yyi9aYCQBOIvg==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/random": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/random/-/random-6.5.0.tgz",
+            "integrity": "sha512-8Q25gQ/XbA7HJAe+eXp4UhcXM9aOOJFaxZ02+XSNwMvY8gtWSCBLVqRcW4OhqilgZ8PeuQDWgBxeo+BIqqFWFQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/rectangle-grid": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-6.5.0.tgz",
+            "integrity": "sha512-yQZ/1vbW68O2KsSB3OZYK+72aWz/Adnf7m2CMKcC+aq6TwjxZjAvlbCOsNUnMAuldRUVN1ph6RXMG4e9KEvKvg==",
+            "requires": {
+                "@turf/boolean-intersects": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/rewind": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-6.5.0.tgz",
+            "integrity": "sha512-IoUAMcHWotBWYwSYuYypw/LlqZmO+wcBpn8ysrBNbazkFNkLf3btSDZMkKJO/bvOzl55imr/Xj4fi3DdsLsbzQ==",
+            "requires": {
+                "@turf/boolean-clockwise": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/rhumb-bearing": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.5.0.tgz",
+            "integrity": "sha512-jMyqiMRK4hzREjQmnLXmkJ+VTNTx1ii8vuqRwJPcTlKbNWfjDz/5JqJlb5NaFDcdMpftWovkW5GevfnuzHnOYA==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/rhumb-destination": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-6.5.0.tgz",
+            "integrity": "sha512-RHNP1Oy+7xTTdRrTt375jOZeHceFbjwohPHlr9Hf68VdHHPMAWgAKqiX2YgSWDcvECVmiGaBKWus1Df+N7eE4Q==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/rhumb-distance": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.5.0.tgz",
+            "integrity": "sha512-oKp8KFE8E4huC2Z1a1KNcFwjVOqa99isxNOwfo4g3SUABQ6NezjKDDrnvC4yI5YZ3/huDjULLBvhed45xdCrzg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/sample": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-6.5.0.tgz",
+            "integrity": "sha512-kSdCwY7el15xQjnXYW520heKUrHwRvnzx8ka4eYxX9NFeOxaFITLW2G7UtXb6LJK8mmPXI8Aexv23F2ERqzGFg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/sector": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-6.5.0.tgz",
+            "integrity": "sha512-cYUOkgCTWqa23SOJBqxoFAc/yGCUsPRdn/ovbRTn1zNTm/Spmk6hVB84LCKOgHqvSF25i0d2kWqpZDzLDdAPbw==",
+            "requires": {
+                "@turf/circle": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/line-arc": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/shortest-path": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-6.5.0.tgz",
+            "integrity": "sha512-4de5+G7+P4hgSoPwn+SO9QSi9HY5NEV/xRJ+cmoFVRwv2CDsuOPDheHKeuIAhKyeKDvPvPt04XYWbac4insJMg==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/bbox-polygon": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/clean-coords": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/transform-scale": "^6.5.0"
+            }
+        },
+        "@turf/simplify": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-6.5.0.tgz",
+            "integrity": "sha512-USas3QqffPHUY184dwQdP8qsvcVH/PWBYdXY5am7YTBACaQOMAlf6AKJs9FT8jiO6fQpxfgxuEtwmox+pBtlOg==",
+            "requires": {
+                "@turf/clean-coords": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/square": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/square/-/square-6.5.0.tgz",
+            "integrity": "sha512-BM2UyWDmiuHCadVhHXKIx5CQQbNCpOxB6S/aCNOCLbhCeypKX5Q0Aosc5YcmCJgkwO5BERCC6Ee7NMbNB2vHmQ==",
+            "requires": {
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/square-grid": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-6.5.0.tgz",
+            "integrity": "sha512-mlR0ayUdA+L4c9h7p4k3pX6gPWHNGuZkt2c5II1TJRmhLkW2557d6b/Vjfd1z9OVaajb1HinIs1FMSAPXuuUrA==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/rectangle-grid": "^6.5.0"
+            }
+        },
+        "@turf/standard-deviational-ellipse": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-6.5.0.tgz",
+            "integrity": "sha512-02CAlz8POvGPFK2BKK8uHGUk/LXb0MK459JVjKxLC2yJYieOBTqEbjP0qaWhiBhGzIxSMaqe8WxZ0KvqdnstHA==",
+            "requires": {
+                "@turf/center-mean": "^6.5.0",
+                "@turf/ellipse": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/points-within-polygon": "^6.5.0"
+            }
+        },
+        "@turf/tag": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-6.5.0.tgz",
+            "integrity": "sha512-XwlBvrOV38CQsrNfrxvBaAPBQgXMljeU0DV8ExOyGM7/hvuGHJw3y8kKnQ4lmEQcmcrycjDQhP7JqoRv8vFssg==",
+            "requires": {
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/tesselate": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-6.5.0.tgz",
+            "integrity": "sha512-M1HXuyZFCfEIIKkglh/r5L9H3c5QTEsnMBoZOFQiRnGPGmJWcaBissGb7mTFX2+DKE7FNWXh4TDnZlaLABB0dQ==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "earcut": "^2.0.0"
+            }
+        },
+        "@turf/tin": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-6.5.0.tgz",
+            "integrity": "sha512-YLYikRzKisfwj7+F+Tmyy/LE3d2H7D4kajajIfc9mlik2+esG7IolsX/+oUz1biguDYsG0DUA8kVYXDkobukfg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/transform-rotate": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-6.5.0.tgz",
+            "integrity": "sha512-A2Ip1v4246ZmpssxpcL0hhiVBEf4L8lGnSPWTgSv5bWBEoya2fa/0SnFX9xJgP40rMP+ZzRaCN37vLHbv1Guag==",
+            "requires": {
+                "@turf/centroid": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/rhumb-bearing": "^6.5.0",
+                "@turf/rhumb-destination": "^6.5.0",
+                "@turf/rhumb-distance": "^6.5.0"
+            }
+        },
+        "@turf/transform-scale": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-6.5.0.tgz",
+            "integrity": "sha512-VsATGXC9rYM8qTjbQJ/P7BswKWXHdnSJ35JlV4OsZyHBMxJQHftvmZJsFbOqVtQnIQIzf2OAly6rfzVV9QLr7g==",
+            "requires": {
+                "@turf/bbox": "^6.5.0",
+                "@turf/center": "^6.5.0",
+                "@turf/centroid": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/rhumb-bearing": "^6.5.0",
+                "@turf/rhumb-destination": "^6.5.0",
+                "@turf/rhumb-distance": "^6.5.0"
+            }
+        },
+        "@turf/transform-translate": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-6.5.0.tgz",
+            "integrity": "sha512-NABLw5VdtJt/9vSstChp93pc6oel4qXEos56RBMsPlYB8hzNTEKYtC146XJvyF4twJeeYS8RVe1u7KhoFwEM5w==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/rhumb-destination": "^6.5.0"
+            }
+        },
+        "@turf/triangle-grid": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-6.5.0.tgz",
+            "integrity": "sha512-2jToUSAS1R1htq4TyLQYPTIsoy6wg3e3BQXjm2rANzw4wPQCXGOxrur1Fy9RtzwqwljlC7DF4tg0OnWr8RjmfA==",
+            "requires": {
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/intersect": "^6.5.0"
+            }
+        },
+        "@turf/truncate": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-6.5.0.tgz",
+            "integrity": "sha512-pFxg71pLk+eJj134Z9yUoRhIi8vqnnKvCYwdT4x/DQl/19RVdq1tV3yqOT3gcTQNfniteylL5qV1uTBDV5sgrg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0"
+            }
+        },
+        "@turf/turf": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-6.5.0.tgz",
+            "integrity": "sha512-ipMCPnhu59bh92MNt8+pr1VZQhHVuTMHklciQURo54heoxRzt1neNYZOBR6jdL+hNsbDGAECMuIpAutX+a3Y+w==",
+            "requires": {
+                "@turf/along": "^6.5.0",
+                "@turf/angle": "^6.5.0",
+                "@turf/area": "^6.5.0",
+                "@turf/bbox": "^6.5.0",
+                "@turf/bbox-clip": "^6.5.0",
+                "@turf/bbox-polygon": "^6.5.0",
+                "@turf/bearing": "^6.5.0",
+                "@turf/bezier-spline": "^6.5.0",
+                "@turf/boolean-clockwise": "^6.5.0",
+                "@turf/boolean-contains": "^6.5.0",
+                "@turf/boolean-crosses": "^6.5.0",
+                "@turf/boolean-disjoint": "^6.5.0",
+                "@turf/boolean-equal": "^6.5.0",
+                "@turf/boolean-intersects": "^6.5.0",
+                "@turf/boolean-overlap": "^6.5.0",
+                "@turf/boolean-parallel": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/boolean-point-on-line": "^6.5.0",
+                "@turf/boolean-within": "^6.5.0",
+                "@turf/buffer": "^6.5.0",
+                "@turf/center": "^6.5.0",
+                "@turf/center-mean": "^6.5.0",
+                "@turf/center-median": "^6.5.0",
+                "@turf/center-of-mass": "^6.5.0",
+                "@turf/centroid": "^6.5.0",
+                "@turf/circle": "^6.5.0",
+                "@turf/clean-coords": "^6.5.0",
+                "@turf/clone": "^6.5.0",
+                "@turf/clusters": "^6.5.0",
+                "@turf/clusters-dbscan": "^6.5.0",
+                "@turf/clusters-kmeans": "^6.5.0",
+                "@turf/collect": "^6.5.0",
+                "@turf/combine": "^6.5.0",
+                "@turf/concave": "^6.5.0",
+                "@turf/convex": "^6.5.0",
+                "@turf/destination": "^6.5.0",
+                "@turf/difference": "^6.5.0",
+                "@turf/dissolve": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/distance-weight": "^6.5.0",
+                "@turf/ellipse": "^6.5.0",
+                "@turf/envelope": "^6.5.0",
+                "@turf/explode": "^6.5.0",
+                "@turf/flatten": "^6.5.0",
+                "@turf/flip": "^6.5.0",
+                "@turf/great-circle": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/hex-grid": "^6.5.0",
+                "@turf/interpolate": "^6.5.0",
+                "@turf/intersect": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/isobands": "^6.5.0",
+                "@turf/isolines": "^6.5.0",
+                "@turf/kinks": "^6.5.0",
+                "@turf/length": "^6.5.0",
+                "@turf/line-arc": "^6.5.0",
+                "@turf/line-chunk": "^6.5.0",
+                "@turf/line-intersect": "^6.5.0",
+                "@turf/line-offset": "^6.5.0",
+                "@turf/line-overlap": "^6.5.0",
+                "@turf/line-segment": "^6.5.0",
+                "@turf/line-slice": "^6.5.0",
+                "@turf/line-slice-along": "^6.5.0",
+                "@turf/line-split": "^6.5.0",
+                "@turf/line-to-polygon": "^6.5.0",
+                "@turf/mask": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "@turf/midpoint": "^6.5.0",
+                "@turf/moran-index": "^6.5.0",
+                "@turf/nearest-point": "^6.5.0",
+                "@turf/nearest-point-on-line": "^6.5.0",
+                "@turf/nearest-point-to-line": "^6.5.0",
+                "@turf/planepoint": "^6.5.0",
+                "@turf/point-grid": "^6.5.0",
+                "@turf/point-on-feature": "^6.5.0",
+                "@turf/point-to-line-distance": "^6.5.0",
+                "@turf/points-within-polygon": "^6.5.0",
+                "@turf/polygon-smooth": "^6.5.0",
+                "@turf/polygon-tangents": "^6.5.0",
+                "@turf/polygon-to-line": "^6.5.0",
+                "@turf/polygonize": "^6.5.0",
+                "@turf/projection": "^6.5.0",
+                "@turf/random": "^6.5.0",
+                "@turf/rewind": "^6.5.0",
+                "@turf/rhumb-bearing": "^6.5.0",
+                "@turf/rhumb-destination": "^6.5.0",
+                "@turf/rhumb-distance": "^6.5.0",
+                "@turf/sample": "^6.5.0",
+                "@turf/sector": "^6.5.0",
+                "@turf/shortest-path": "^6.5.0",
+                "@turf/simplify": "^6.5.0",
+                "@turf/square": "^6.5.0",
+                "@turf/square-grid": "^6.5.0",
+                "@turf/standard-deviational-ellipse": "^6.5.0",
+                "@turf/tag": "^6.5.0",
+                "@turf/tesselate": "^6.5.0",
+                "@turf/tin": "^6.5.0",
+                "@turf/transform-rotate": "^6.5.0",
+                "@turf/transform-scale": "^6.5.0",
+                "@turf/transform-translate": "^6.5.0",
+                "@turf/triangle-grid": "^6.5.0",
+                "@turf/truncate": "^6.5.0",
+                "@turf/union": "^6.5.0",
+                "@turf/unkink-polygon": "^6.5.0",
+                "@turf/voronoi": "^6.5.0"
+            }
+        },
+        "@turf/union": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/union/-/union-6.5.0.tgz",
+            "integrity": "sha512-igYWCwP/f0RFHIlC2c0SKDuM/ObBaqSljI3IdV/x71805QbIvY/BYGcJdyNcgEA6cylIGl/0VSlIbpJHZ9ldhw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "polygon-clipping": "^0.15.3"
+            }
+        },
+        "@turf/unkink-polygon": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-6.5.0.tgz",
+            "integrity": "sha512-8QswkzC0UqKmN1DT6HpA9upfa1HdAA5n6bbuzHy8NJOX8oVizVAqfEPY0wqqTgboDjmBR4yyImsdPGUl3gZ8JQ==",
+            "requires": {
+                "@turf/area": "^6.5.0",
+                "@turf/boolean-point-in-polygon": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "rbush": "^2.0.1"
+            },
+            "dependencies": {
+                "quickselect": {
+                    "version": "1.1.1",
+                    "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz",
+                    "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ=="
+                },
+                "rbush": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz",
+                    "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==",
+                    "requires": {
+                        "quickselect": "^1.0.1"
+                    }
+                }
+            }
+        },
+        "@turf/voronoi": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-6.5.0.tgz",
+            "integrity": "sha512-C/xUsywYX+7h1UyNqnydHXiun4UPjK88VDghtoRypR9cLlb7qozkiLRphQxxsCM0KxyxpVPHBVQXdAL3+Yurow==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "d3-voronoi": "1.1.2"
+            }
+        },
         "@types/adm-zip": {
             "version": "0.4.34",
             "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
@@ -9921,11 +11209,6 @@
             "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
             "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w=="
         },
-        "cheap-ruler": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-3.0.2.tgz",
-            "integrity": "sha512-02T332h1/HTN6cDSufLP8x4JzDs2+VC+8qZ/N0kWIVPyc2xUkWwWh3B2fJxR7raXkL4Mq7k554mfuM9ofv/vGg=="
-        },
         "check-error": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -10539,6 +11822,24 @@
                 }
             }
         },
+        "concaveman": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz",
+            "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==",
+            "requires": {
+                "point-in-polygon": "^1.1.0",
+                "rbush": "^3.0.1",
+                "robust-predicates": "^2.0.4",
+                "tinyqueue": "^2.0.3"
+            },
+            "dependencies": {
+                "robust-predicates": {
+                    "version": "2.0.4",
+                    "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz",
+                    "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg=="
+                }
+            }
+        },
         "configstore": {
             "version": "3.1.5",
             "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz",
@@ -11205,9 +12506,9 @@
             }
         },
         "d3": {
-            "version": "7.8.4",
-            "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
-            "integrity": "sha512-q2WHStdhiBtD8DMmhDPyJmXUxr6VWRngKyiJ5EfXMxPw+tqT6BhNjhJZ4w3BHsNm3QoVfZLY8Orq/qPFczwKRA==",
+            "version": "7.8.5",
+            "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz",
+            "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==",
             "requires": {
                 "d3-array": "3",
                 "d3-axis": "3",
@@ -11462,6 +12763,11 @@
                 "d3-timer": "1 - 3"
             }
         },
+        "d3-voronoi": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
+            "integrity": "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw=="
+        },
         "d3-zoom": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
@@ -11826,6 +13132,11 @@
             "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
             "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
         },
+        "density-clustering": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz",
+            "integrity": "sha512-icpmBubVTwLnsaor9qH/4tG5+7+f61VcqMN3V3pm9sxxSCt2Jcs0zWOgwZW9ARJYaKD3FumIgHiMOcIMRRAzFQ=="
+        },
         "depcheck": {
             "version": "0.9.2",
             "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.9.2.tgz",
@@ -15290,6 +16601,33 @@
             "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz",
             "integrity": "sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ=="
         },
+        "geojson-equality": {
+            "version": "0.1.6",
+            "resolved": "https://registry.npmjs.org/geojson-equality/-/geojson-equality-0.1.6.tgz",
+            "integrity": "sha512-TqG8YbqizP3EfwP5Uw4aLu6pKkg6JQK9uq/XZ1lXQntvTHD1BBKJWhNpJ2M0ax6TuWMP3oyx6Oq7FCIfznrgpQ==",
+            "requires": {
+                "deep-equal": "^1.0.0"
+            }
+        },
+        "geojson-rbush": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-3.2.0.tgz",
+            "integrity": "sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w==",
+            "requires": {
+                "@turf/bbox": "*",
+                "@turf/helpers": "6.x",
+                "@turf/meta": "6.x",
+                "@types/geojson": "7946.0.8",
+                "rbush": "^3.0.1"
+            },
+            "dependencies": {
+                "@types/geojson": {
+                    "version": "7946.0.8",
+                    "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
+                    "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
+                }
+            }
+        },
         "geojson-vt": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
@@ -18715,9 +20053,9 @@
             }
         },
         "mapbox-gl": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.0.1.tgz",
-            "integrity": "sha512-o7C6sAlj6Hkdd4xQVEgQflgJYNYyZOAtYahhIOb9m8chI8umtWcCp8Ie0iGLYJvce1WHRMa3WGzs69ggwuWlDA==",
+            "version": "2.15.0",
+            "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-2.15.0.tgz",
+            "integrity": "sha512-fjv+aYrd5TIHiL7wRa+W7KjtUqKWziJMZUkK5hm8TvJ3OLeNPx4NmW/DgfYhd/jHej8wWL+QJBDbdMMAKvNC0A==",
             "requires": {
                 "@mapbox/geojson-rewind": "^0.5.2",
                 "@mapbox/jsonlint-lines-primitives": "^2.0.2",
@@ -18727,7 +20065,6 @@
                 "@mapbox/unitbezier": "^0.0.1",
                 "@mapbox/vector-tile": "^1.3.1",
                 "@mapbox/whoots-js": "^3.1.0",
-                "cheap-ruler": "^3.0.1",
                 "csscolorparser": "~1.0.3",
                 "earcut": "^2.2.4",
                 "geojson-vt": "^3.2.1",
@@ -25366,6 +26703,19 @@
             "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
             "dev": true
         },
+        "point-in-polygon": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
+            "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="
+        },
+        "polygon-clipping": {
+            "version": "0.15.3",
+            "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz",
+            "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==",
+            "requires": {
+                "splaytree": "^3.1.0"
+            }
+        },
         "popper.js": {
             "version": "1.16.1",
             "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
@@ -26218,6 +27568,14 @@
                 "schema-utils": "^1.0.0"
             }
         },
+        "rbush": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
+            "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
+            "requires": {
+                "quickselect": "^2.0.0"
+            }
+        },
         "rc": {
             "version": "1.2.8",
             "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -27649,9 +29007,9 @@
             }
         },
         "robust-predicates": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
-            "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+            "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
         },
         "rope-sequence": {
             "version": "1.3.3",
@@ -28341,6 +29699,11 @@
                 }
             }
         },
+        "skmeans": {
+            "version": "0.9.7",
+            "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
+            "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="
+        },
         "slash": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -28852,6 +30215,11 @@
                 }
             }
         },
+        "splaytree": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz",
+            "integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A=="
+        },
         "split-on-first": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
@@ -29652,6 +31020,36 @@
             "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz",
             "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo="
         },
+        "topojson-client": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz",
+            "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==",
+            "requires": {
+                "commander": "2"
+            },
+            "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                }
+            }
+        },
+        "topojson-server": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz",
+            "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==",
+            "requires": {
+                "commander": "2"
+            },
+            "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                }
+            }
+        },
         "touch": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
@@ -30107,6 +31505,11 @@
                 "safe-buffer": "^5.0.1"
             }
         },
+        "turf-jsts": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/turf-jsts/-/turf-jsts-1.2.3.tgz",
+            "integrity": "sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA=="
+        },
         "tweetnacl": {
             "version": "0.14.5",
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
diff --git a/package.json b/package.json
index 42d43e758..9a9e7675d 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
         "@types/jquery": "^3.5.14",
         "@types/libxmljs": "^0.18.7",
         "@types/lodash": "^4.14.179",
-        "@types/mapbox-gl": "^2.7.19",
+        "@types/mapbox-gl": "^2.7.17",
         "@types/mobile-detect": "^1.3.4",
         "@types/mocha": "^5.2.6",
         "@types/mongodb": "^3.6.20",
@@ -151,6 +151,7 @@
         "@octokit/core": "^4.0.4",
         "@react-google-maps/api": "^2.7.0",
         "@react-three/fiber": "^6.2.3",
+        "@turf/turf": "^6.5.0",
         "@types/bezier-js": "^4.1.0",
         "@types/cors": "^2.8.12",
         "@types/d3-axis": "^2.1.3",
@@ -199,7 +200,7 @@
         "cors": "^2.8.5",
         "csv-parser": "^3.0.0",
         "csv-stringify": "^6.3.0",
-        "d3": "^7.6.1",
+        "d3": "^7.8.5",
         "debounce": "^2.0.0",
         "depcheck": "^0.9.2",
         "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
@@ -243,7 +244,7 @@
         "jsonschema": "^1.4.0",
         "jszip": "^3.7.1",
         "lodash": "^4.17.21",
-        "mapbox-gl": "^3.0.1",
+        "mapbox-gl": "^2.15.0",
         "material-ui": "^0.20.2",
         "md5-file": "^5.0.0",
         "memorystream": "^0.3.1",
@@ -302,7 +303,7 @@
         "react-jsx-parser": "^1.29.0",
         "react-loader-spinner": "^5.3.4",
         "react-loading": "^2.0.3",
-        "react-map-gl": "^7.1.0",
+        "react-map-gl": "^7.1.6",
         "react-markdown": "^8.0.3",
         "react-measure": "^2.5.2",
         "react-refresh-typescript": "^2.0.7",
diff --git a/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx b/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx
new file mode 100644
index 000000000..d54a175b2
--- /dev/null
+++ b/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx
@@ -0,0 +1,35 @@
+import * as React from "react";
+
+export const slowSpeedIcon: JSX.Element = (
+    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 435.62">
+        <defs>
+        <style type="text/css">
+            {`
+                .fil0 { fill: black; fill-rule: nonzero; }
+                .fil1 { fill: #FE0000; fill-rule: nonzero; }
+            `}
+        </style>
+        </defs>
+        <path className="fil0" d="M174.84 343.06c-7.31,-13.12 -13.03,-27.28 -16.89,-42.18 -3.76,-14.56 -5.77,-29.71 -5.77,-45.17 0,-11.94 1.19,-23.66 3.43,-35.03 2.29,-11.57 5.74,-22.83 10.2,-33.63 13.7,-33.14 37.01,-61.29 66.42,-80.96 25.38,-16.96 55.28,-27.66 87.45,-29.87l0 -30.17c0,-0.46 0.02,-0.92 0.06,-1.37l-33.7 0c-5.53,0 -10.05,-4.52 -10.05,-10.04l0 -24.59c0,-5.53 4.52,-10.05 10.05,-10.05l101.27 0c5.53,0 10.05,4.52 10.05,10.05l0 24.59c0,5.52 -4.52,10.04 -10.05,10.04l-33.69 0c0.03,0.45 0.05,0.91 0.05,1.37l0 31.03 -0.1 0c41.1,4.89 77.94,23.63 105.73,51.42 32.56,32.55 52.7,77.54 52.7,127.21 0,49.67 -20.14,94.66 -52.7,127.21 -32.55,32.55 -77.54,52.7 -127.21,52.7 -33.16,0 -64.29,-9.04 -91.05,-24.78 -27.66,-16.27 -50.59,-39.73 -66.2,-67.78zm148.42 -36.62l-80.33 0 0 -25.71 28.6 0 0 -42.57 -28.6 1.93 0 -25.71 36.95 -8.35 25.38 0 0 74.7 18 0 0 25.71zm44.34 -100.41l11.08 26.83 1.61 0 11.09 -26.83 34.86 0 -22.33 48.52 22.33 51.89 -35.67 0 -12.05 -28.92 -1.44 0 -11.89 28.92 -34.06 0 21.85 -50.93 -21.85 -49.48 36.47 0zm126.08 -74.6c6.98,-16.66 6.15,-34.13 -3.84,-45.82 -12,-14.03 -33.67,-15.64 -53.8,-5.77 21.32,14.62 40.68,31.63 57.64,51.59zm-323.17 0c-6.98,-16.66 -6.16,-34.13 3.84,-45.82 11.99,-14.03 33.67,-15.64 53.79,-5.77 -21.32,14.62 -40.68,31.63 -57.63,51.59zm15.31 162.23c3.23,12.5 8.04,24.39 14.18,35.42 13.13,23.58 32.39,43.29 55.6,56.94 22.37,13.16 48.52,20.71 76.49,20.71 41.71,0 79.47,-16.9 106.8,-44.23 27.32,-27.32 44.23,-65.08 44.23,-106.79 0,-41.71 -16.91,-79.47 -44.23,-106.8 -27.33,-27.32 -65.09,-44.23 -106.8,-44.23 -31.07,0 -59.91,9.34 -83.84,25.33 -24.74,16.54 -44.33,40.19 -55.82,67.98 -3.68,8.91 -6.56,18.35 -8.5,28.22 -1.87,9.49 -2.86,19.36 -2.86,29.5 0,13.24 1.65,25.96 4.75,37.95z"/>
+        <path className="fil1" d="M55.23 188.52c-7.98,0 -14.45,-6.47 -14.45,-14.44 0,-7.98 6.47,-14.45 14.45,-14.45l63.94 0c7.98,0 14.45,6.47 14.45,14.45 0,7.97 -6.47,14.44 -14.45,14.44l-63.94 0zm0.72 167.68c-7.97,0 -14.44,-6.47 -14.44,-14.45 0,-7.97 6.47,-14.45 14.44,-14.45l64.58 0c7.97,0 14.45,6.48 14.45,14.45 0,7.98 -6.48,14.45 -14.45,14.45l-64.58 0zm-41.5 -84.94c-7.98,0 -14.45,-6.47 -14.45,-14.45 0,-7.97 6.47,-14.44 14.45,-14.44l89.12 0c7.98,0 14.45,6.47 14.45,14.44 0,7.98 -6.47,14.45 -14.45,14.45l-89.12 0z"/>
+    </svg>
+);
+
+export const mediumSpeedIcon: JSX.Element = (
+    <svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 122.88 104.55">
+        <defs><style>{`.cls-1{fill:#fe0000;}`}</style></defs>
+        <path d="M42,82.34a42.82,42.82,0,0,1-4.05-10.13A43.2,43.2,0,0,1,76.72,18.29V11.05c0-.11,0-.22,0-.33H68.65a2.41,2.41,0,0,1-2.41-2.41V2.41A2.41,2.41,0,0,1,68.65,0H93a2.42,2.42,0,0,1,2.42,2.41v5.9A2.42,2.42,0,0,1,93,10.72H84.87c0,.11,0,.22,0,.33V18.5h0A43.17,43.17,0,1,1,42,82.34ZM88.22,49.45l2.66,6.44h.39l2.66-6.44h8.37L96.94,61.09l5.36,12.45H93.74L90.85,66.6H90.5l-2.85,6.94H79.47l5.25-12.22L79.47,49.45ZM58.65,56.08l-1-5.75a33.58,33.58,0,0,1,9.68-1.46c1.28,0,2.35,0,3.22.11a11.77,11.77,0,0,1,2.67.58,5.41,5.41,0,0,1,2.2,1.28c1.24,1.23,1.85,3.12,1.85,5.66s-.72,4.42-2.16,5.63S70.64,64.73,66,66.3v1.08H76.89v6.16H57.11V68.72a10.73,10.73,0,0,1,.81-4.12,8.4,8.4,0,0,1,2.43-2.7,12.13,12.13,0,0,1,2.79-1.7l3.32-1.52c1-.47,1.88-.87,2.52-1.17V55.42a28.59,28.59,0,0,0-3.2-.19,30.66,30.66,0,0,0-7.13.85Zm59.83-24.54c1.68-4,1.48-8.19-.92-11-2.88-3.37-8.08-3.76-12.91-1.39a69.74,69.74,0,0,1,13.83,12.38Zm-77.56,0c-1.67-4-1.48-8.19.92-11,2.88-3.37,8.08-3.76,12.91-1.39A70,70,0,0,0,40.92,31.54ZM44.6,70.48A36,36,0,0,0,48,79a35.91,35.91,0,1,0-3.4-8.5Z"/>
+        <path className="cls-1" d="M13.25,45.25a3.47,3.47,0,0,1,0-6.94H28.6a3.47,3.47,0,0,1,0,6.94Z"/>
+        <path className="cls-1" d="M3.47,65.1a3.47,3.47,0,1,1,0-6.93H24.86a3.47,3.47,0,0,1,0,6.93Z"/>
+        <path className="cls-1" d="M13.43,85.49a3.47,3.47,0,1,1,0-6.94h15.5a3.47,3.47,0,0,1,0,6.94Z"/>
+    </svg>
+);
+
+export const fastSpeedIcon: JSX.Element = (
+    <svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 122.88 104.55">
+        <defs><style>{`.cls-1{fill:#fe0000;`}</style></defs>
+        <path d="M42,82.34a42.82,42.82,0,0,1-4.05-10.13A43.2,43.2,0,0,1,76.72,18.29V11.05c0-.11,0-.22,0-.33H68.65a2.41,2.41,0,0,1-2.41-2.41V2.41A2.41,2.41,0,0,1,68.65,0H93a2.42,2.42,0,0,1,2.42,2.41v5.9A2.42,2.42,0,0,1,93,10.72H84.87c0,.11,0,.22,0,.33V18.5h0A43.17,43.17,0,1,1,42,82.34ZM88.22,49.61l2.66,6.44h.39l2.66-6.44h8.37L96.94,61.26l5.36,12.45H93.74l-2.9-6.94H90.5l-2.86,6.94H79.47l5.24-12.22L79.47,49.61Zm-19,8.48v-2.5a24.92,24.92,0,0,0-3.74-.2A33.25,33.25,0,0,0,59,56.2l-1-5.7A30.47,30.47,0,0,1,67.13,49a22.86,22.86,0,0,1,5.48.47,6.91,6.91,0,0,1,2.5,1.11,5.62,5.62,0,0,1,1.78,4.55,5.84,5.84,0,0,1-3.2,5.56v.19a5.73,5.73,0,0,1,3.81,5.74,8.67,8.67,0,0,1-.63,3.49,6,6,0,0,1-1.6,2.24,7.15,7.15,0,0,1-2.55,1.25,25.64,25.64,0,0,1-6.61.66,37.78,37.78,0,0,1-8.54-1l1.08-6.37a27.22,27.22,0,0,0,6.21.89,35.79,35.79,0,0,0,4.35-.23V65.11l-6.63-.65V58.87l6.63-.78Zm49.27-26.55c1.68-4,1.48-8.19-.92-11-2.88-3.37-8.08-3.76-12.91-1.39a69.74,69.74,0,0,1,13.83,12.38Zm-77.56,0c-1.67-4-1.48-8.19.92-11,2.88-3.37,8.08-3.76,12.91-1.39A70,70,0,0,0,40.92,31.54ZM44.6,70.48A36,36,0,0,0,48,79a35.91,35.91,0,1,0-3.4-8.5Z"/>
+        <path className="cls-1" d="M13.25,45.25a3.47,3.47,0,0,1,0-6.94H28.6a3.47,3.47,0,0,1,0,6.94Zm.18,40.24a3.47,3.47,0,1,1,0-6.94h15.5a3.47,3.47,0,0,1,0,6.94ZM3.47,65.1a3.47,3.47,0,1,1,0-6.93H24.86a3.47,3.47,0,0,1,0,6.93Z"/>
+    </svg>
+);
+
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
new file mode 100644
index 000000000..256acbf13
--- /dev/null
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -0,0 +1,429 @@
+import mapboxgl from "mapbox-gl";
+import { MercatorCoordinate } from "mapbox-gl";
+import { MapRef } from "react-map-gl";
+import * as React from 'react';
+import * as d3 from "d3";
+import * as turf from '@turf/turf';
+import { Position } from "@turf/turf";
+import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson";
+import { observer } from "mobx-react";
+import { action, computed, observable } from "mobx";
+
+export enum AnimationStatus {
+    START = 'start',
+    RESUME = 'resume',
+    RESTART = 'restart',
+}
+
+export enum AnimationSpeed {
+    SLOW = '1x',
+    MEDIUM = '2x',
+    FAST = '3x',
+}
+
+@observer
+export class AnimationUtility {
+    private SMOOTH_FACTOR = 0.95
+    private ROUTE_COORDINATES: Position[] = []; 
+    private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>;
+    private FLY_IN_START_PITCH = 40;
+    private FIRST_LNG_LAT: {lng: number, lat: number};
+    private START_ALTITUDE = 3_000_000;
+
+    @observable private isStreetViewAnimation: boolean;
+    @observable private animationSpeed: AnimationSpeed;
+
+    
+    private previousLngLat: {lng: number, lat: number};
+
+    private currentStreetViewBearing: number = 0;
+
+    @computed get flyInEndBearing() {
+        return this.isStreetViewAnimation ? 
+            this.calculateBearing(
+                {
+                    lng: this.ROUTE_COORDINATES[0][0],
+                    lat: this.ROUTE_COORDINATES[0][1]
+                },
+                {
+                    lng: this.ROUTE_COORDINATES[1][0],
+                    lat: this.ROUTE_COORDINATES[1][1]
+                }
+            )
+            : -20;
+        }
+
+    @computed get flyInStartBearing() {
+        return Math.max(0, Math.min(this.flyInEndBearing + 20, 360)); // between 0 and 360
+    }
+
+    @computed get flyInEndAltitude() {
+        return this.isStreetViewAnimation ? 70 : 10000;
+    }
+
+    @computed get flyInEndPitch() {
+        return this.isStreetViewAnimation ? 80 : 50;
+    }
+
+    @computed get flyToDuration() {
+        switch (this.animationSpeed) {
+            case AnimationSpeed.SLOW:
+                return 4000;
+            case AnimationSpeed.MEDIUM:
+                return 2500;
+            case AnimationSpeed.FAST:
+                return 1250;
+            default:
+                return 2500;
+        }
+    }
+
+    @computed get animationDuration(): number {
+        return 20_000;
+        // compute path distance for a standard speed 
+        // get animation speed 
+        // get isStreetViewAnimation (should be slower if so)
+    }
+
+    @action
+    public updateAnimationSpeed(speed: AnimationSpeed) {
+        // calculate new flyToDuration and animationDuration
+        this.animationSpeed = speed;
+    }
+    
+    @action
+    public updateIsStreetViewAnimation(isStreetViewAnimation: boolean) {
+        this.isStreetViewAnimation = isStreetViewAnimation;
+    }
+
+
+    constructor(
+        firstLngLat: {lng: number, lat: number},
+        routeCoordinates: Position[],
+        isStreetViewAnimation: boolean,
+        animationSpeed: AnimationSpeed
+    ) {
+        this.FIRST_LNG_LAT = firstLngLat;
+        this.previousLngLat = firstLngLat;
+        this.ROUTE_COORDINATES = routeCoordinates;
+        this.PATH = turf.lineString(routeCoordinates);
+
+        const bearing = this.calculateBearing(
+            {
+                lng: routeCoordinates[0][0],
+                lat: routeCoordinates[0][1]
+            },
+            {
+                lng: routeCoordinates[1][0],
+                lat: routeCoordinates[1][1]
+            }
+        );
+        this.currentStreetViewBearing = bearing;
+        // if (isStreetViewAnimation){
+        //     this.flyInEndBearing = bearing;
+        // }
+        this.isStreetViewAnimation = isStreetViewAnimation;
+        this.animationSpeed = animationSpeed;
+        // calculate animation duration based on speed
+        // this.animationDuration = animationDuration;
+    }
+
+    public animatePath = async ({
+        map,
+        // path,
+        // startBearing,
+        // startAltitude,
+        // pitch,
+        currentAnimationPhase,
+        updateAnimationPhase,
+        updateFrameId,
+      }: {
+        map: MapRef,
+        // path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>,
+        // startBearing: number,
+        // startAltitude: number,
+        // pitch: number,
+        currentAnimationPhase: number,
+        updateAnimationPhase: ( 
+            newAnimationPhase: number,
+            ) => void,
+        updateFrameId: (newFrameId: number) => void;
+
+      }) => {
+        return new Promise<void>(async (resolve) => {
+          const pathDistance = turf.lineDistance(this.PATH);
+          console.log("PATH DISTANCE: ", pathDistance);
+          let startTime: number | null = null;
+      
+          const frame = async (currentTime: number) => {
+            if (!startTime) startTime = currentTime;
+            const elapsedSinceLastFrame = currentTime - startTime;
+            const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
+            const animationPhase = currentAnimationPhase + phaseIncrement;
+      
+            // when the duration is complete, resolve the promise and stop iterating
+            if (animationPhase > 1) {
+              resolve();
+              return;
+            }
+
+      
+            // calculate the distance along the path based on the animationPhase
+            const alongPath = turf.along(this.PATH, pathDistance * animationPhase).geometry
+              .coordinates;
+      
+            const lngLat = {
+              lng: alongPath[0],
+              lat: alongPath[1],
+            };
+      
+            let bearing: number;
+            if (this.isStreetViewAnimation){
+                bearing = this.lerp(
+                    this.currentStreetViewBearing,
+                    this.calculateBearing(this.previousLngLat, lngLat),
+                    0.028 // Adjust the transition speed as needed
+                  );
+                  this.currentStreetViewBearing = bearing;
+                // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
+            } else {
+                // slowly rotate the map at a constant rate
+                bearing = this.flyInEndBearing - animationPhase * 200.0;
+                // bearing = startBearing - animationPhase * 200.0;
+            }
+
+            this.previousLngLat = lngLat;
+            
+            updateAnimationPhase(animationPhase);
+      
+            // compute corrected camera ground position, so that he leading edge of the path is in view
+            var correctedPosition = this.computeCameraPosition(
+              this.isStreetViewAnimation,
+              this.flyInEndPitch,
+              bearing,
+              lngLat,
+              this.flyInEndAltitude,
+              true // smooth
+            );
+      
+            // set the pitch and bearing of the camera
+            const camera = map.getFreeCameraOptions();
+            camera.setPitchBearing(this.flyInEndPitch, bearing);
+      
+            console.log("Corrected pos: ", correctedPosition);
+            console.log("Start altitude: ", this.flyInEndAltitude);
+            // set the position and altitude of the camera
+            camera.position = MercatorCoordinate.fromLngLat(
+              correctedPosition,
+              this.flyInEndAltitude
+            );
+
+      
+            // apply the new camera options
+            map.setFreeCameraOptions(camera);
+      
+            // repeat!
+            const innerFrameId = await window.requestAnimationFrame(frame);
+            updateFrameId(innerFrameId);
+          };
+      
+          const outerFrameId = await window.requestAnimationFrame(frame);
+          updateFrameId(outerFrameId);
+        });
+      };
+
+    public flyInAndRotate = async ({
+        map,
+        updateFrameId
+      }:
+      {
+        map: MapRef,
+        updateFrameId: (newFrameId: number) => void
+      }
+      ) => {
+        return new Promise<{bearing: number, altitude: number}>(async (resolve) => {
+          let start: number | null;
+      
+          var currentAltitude;
+          var currentBearing;
+          var currentPitch;
+      
+          // the animation frame will run as many times as necessary until the duration has been reached
+          const frame = async (time: number) => {
+            if (!start) {
+              start = time;
+            }
+      
+            // otherwise, use the current time to determine how far along in the duration we are
+            let animationPhase = (time - start) / this.flyToDuration;
+      
+            // because the phase calculation is imprecise, the final zoom can vary
+            // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
+            if (animationPhase > 1) {
+              animationPhase = 1;
+            }
+      
+            currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase)
+            // rotate the camera between startBearing and endBearing
+            currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase)
+      
+            currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase)
+      
+            // compute corrected camera ground position, so the start of the path is always in view
+            var correctedPosition = this.computeCameraPosition(
+              false,
+              currentPitch,
+              currentBearing,
+              this.FIRST_LNG_LAT,
+              currentAltitude
+            );
+      
+            // set the pitch and bearing of the camera
+            const camera = map.getFreeCameraOptions();
+            camera.setPitchBearing(currentPitch, currentBearing);
+      
+            // set the position and altitude of the camera
+            camera.position = MercatorCoordinate.fromLngLat(
+              correctedPosition,
+              currentAltitude
+            );
+      
+            // apply the new camera options
+            map.setFreeCameraOptions(camera);
+      
+            // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
+            if (animationPhase === 1) {
+              resolve({
+                bearing: currentBearing,
+                altitude: currentAltitude,
+              });
+      
+              // return so there are no further iterations of this frame
+              return;
+            }
+      
+            const innerFrameId = await window.requestAnimationFrame(frame);
+            updateFrameId(innerFrameId);
+            
+          };
+      
+         const outerFrameId = await window.requestAnimationFrame(frame);
+         updateFrameId(outerFrameId);
+        });
+      };
+
+      previousCameraPosition: {lng: number, lat: number} | null = null;
+
+      lerp = (start: number, end: number, amt: number) => {
+        return (1 - amt) * start + amt * end;
+      }
+
+      computeCameraPosition = (
+        isStreetViewAnimation: boolean,
+        pitch: number,
+        bearing: number,
+        targetPosition: {lng: number, lat: number},
+        altitude: number,
+        smooth = false
+      ) => {
+        const bearingInRadian = (bearing * Math.PI) / 180;
+        const pitchInRadian = ((90 - pitch)* Math.PI) / 180;
+
+        let correctedLng = targetPosition.lng;
+        let correctedLat = targetPosition.lat;
+
+        if (!isStreetViewAnimation) {
+            const lngDiff =
+                ((altitude / Math.tan(pitchInRadian)) *
+                Math.sin(-bearingInRadian)) /
+                70000; // ~70km/degree longitude
+            const latDiff =
+                ((altitude / Math.tan(pitchInRadian)) *
+                Math.cos(-bearingInRadian)) /
+                110000 // 110km/degree latitude
+
+            correctedLng = targetPosition.lng + lngDiff;
+            correctedLat = targetPosition.lat - latDiff;
+
+        }
+        
+        const newCameraPosition = {
+          lng: correctedLng,
+          lat: correctedLat
+        };
+      
+        if (smooth) {
+          if (this.previousCameraPosition) {
+            newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
+            newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
+          }
+        }
+      
+        this.previousCameraPosition = newCameraPosition
+      
+        return newCameraPosition;
+      };
+      
+      public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties>=> {
+        const coords = {
+          latitude: center[1],
+          longitude: center[0],
+        };
+        const km = radiusInKm;
+        const ret = [];
+        const distanceX = km / (111.320 * Math.cos((coords.latitude * Math.PI) / 180));
+        const distanceY = km / 110.574;
+        let theta;
+        let x;
+        let y;
+        for (let i = 0; i < points; i += 1) {
+          theta = (i / points) * (2 * Math.PI);
+          x = distanceX * Math.cos(theta);
+          y = distanceY * Math.sin(theta);
+          ret.push([coords.longitude + x, coords.latitude + y]);
+        }
+        ret.push(ret[0]);
+        return {
+          type: 'Feature',
+          geometry: {
+            type: 'Polygon',
+            coordinates: [ret],
+          },
+          properties: {}
+        };
+      }
+
+      private calculateBearing(
+        from: { lng: number; lat: number },
+        to: { lng: number; lat: number }
+      ): number {
+        const lon1 = from.lng;
+        const lat1 = from.lat;
+        const lon2 = to.lng;
+        const lat2 = to.lat;
+      
+        const lon1Rad = (lon1 * Math.PI) / 180;
+        const lon2Rad = (lon2 * Math.PI) / 180;
+        const lat1Rad = (lat1 * Math.PI) / 180;
+        const lat2Rad = (lat2 * Math.PI) / 180;
+      
+        const y = Math.sin(lon2Rad - lon1Rad) * Math.cos(lat2Rad);
+        const x =
+          Math.cos(lat1Rad) * Math.sin(lat2Rad) -
+          Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
+      
+        let bearing = Math.atan2(y,x);
+      
+        // Convert bearing from radians to degrees
+        bearing = (bearing * 180) / Math.PI;
+      
+        // Ensure the bearing is within [0, 360)
+        if (bearing < 0) {
+          bearing += 360;
+        }
+      
+        return bearing;
+      }
+
+
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 2c2879900..f4e24d9c1 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -32,11 +32,11 @@ import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/materi
 import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
 import { MapBox } from './MapBox';
 import { List } from '../../../../fields/List';
-import { MapboxColor, MarkerIcons } from './MarkerIcons';
-import { CirclePicker } from 'react-color';
+import { MarkerIcons } from './MarkerIcons';
+import { CirclePicker, ColorState } from 'react-color';
 import { Position } from 'geojson';
 
-type MapAnchorMenuType = 'standard' | 'route' | 'calendar' | 'customize';
+type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' | 'route';
 
 @observer
 export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -61,17 +61,28 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     public DisplayRoute: (routeInfoMap: Record<TransportationType, any> | undefined, type: TransportationType) => void = unimplementedFunction;
     public HideRoute: () => void = unimplementedFunction;
-    public AddNewRouteToMap: (coordinates: Position[], origin: string, destination: string) => void = unimplementedFunction;
+    public AddNewRouteToMap: (coordinates: Position[], origin: string, destination: any, createPinForDestination: boolean) => void = unimplementedFunction;
     public CreatePin: (feature: any) => void = unimplementedFunction;
 
     public UpdateMarkerColor: (color: string) => void = unimplementedFunction;
     public UpdateMarkerIcon: (iconKey: string) => void = unimplementedFunction;
 
+    public OpenAnimationPanel: (routeDoc: Doc | undefined) => void = unimplementedFunction;
+
+    @observable
+    menuType: MapAnchorMenuType = 'standard';
+
+    @action
+    public setMenuType = (menuType: MapAnchorMenuType) => {
+        this.menuType = menuType;
+    }
     
 
     private allMapPinDocs: Doc[] = [];
 
-    private pinDoc: Doc | undefined = undefined 
+    private pinDoc: Doc | undefined = undefined;
+
+    private routeDoc: Doc | undefined = undefined;
 
     private title: string | undefined = undefined;
 
@@ -81,6 +92,11 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`) ;
     }
 
+    public setRouteDoc(routeDoc: Doc){
+        this.routeDoc = routeDoc;
+        this.title = StrCast(routeDoc.title ?? 'Map route')
+    }
+
     public setAllMapboxPins(pinDocs: Doc[]) {
         this.allMapPinDocs = pinDocs;
         pinDocs.forEach((p, idx) => {
@@ -148,12 +164,11 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     //     return this.top
     // }
 
-    @observable
-    menuType: MapAnchorMenuType = 'standard';
+
 
     @action
     DirectionsClick = () => {
-        this.menuType = 'route';
+        this.menuType = 'routeCreation';
     }
 
     @action
@@ -175,6 +190,26 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
           }
     }
 
+    @action 
+    onMarkerColorChange = (color: ColorState) => {
+        if (this.pinDoc){
+            this.pinDoc.markerColor = color.hex;
+        }
+    }
+
+    revertToOriginalMarker = () => {
+        if (this.pinDoc) {
+            this.pinDoc.markerType = "MAP_PIN";
+            this.pinDoc.markerColor = "#ff5722";
+        }
+    }
+
+    onMarkerIconChange = (iconKey: string) => {
+        if (this.pinDoc) {
+            this.pinDoc.markerType = iconKey;
+        }
+    }
+
 
     @observable
     destinationFeatures: any[] = []
@@ -248,7 +283,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         if (this.currentRouteInfoMap && this.selectedTransportationType && this.selectedDestinationFeature){
             const coordinates = this.currentRouteInfoMap[this.selectedTransportationType].coordinates;
             console.log(coordinates);
-            this.AddNewRouteToMap(coordinates, this.title ?? "", this.selectedDestinationFeature.place_name);
+            console.log(this.selectedDestinationFeature);
+            this.AddNewRouteToMap(coordinates, this.title ?? "", this.selectedDestinationFeature, this.createPinForDestination);
             this.HideRoute();
         }
     }
@@ -258,11 +294,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             const markerType = StrCast(this.pinDoc.markerType);
             const markerColor = StrCast(this.pinDoc.markerColor);
 
-            if (markerType.startsWith("MAPBOX")){
-                return MarkerIcons.getMapboxIcon(markerColor as MapboxColor);
-            } else { // font awesome icon
-                return MarkerIcons.getFontAwesomeIcon(markerType, markerColor);
-            }
+            return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor);
         }
         return undefined;
     }
@@ -313,7 +345,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         />
                     </>
                 }
-                {this.menuType === 'route' && 
+                {this.menuType === 'routeCreation' && 
                     <>
                         <IconButton
                             tooltip="Go back" //
@@ -327,19 +359,39 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                             icon={<FontAwesomeIcon icon={faAdd as IconLookup} />}
                             color={SettingsManager.userColor}
                         />
+                    </>
+                }
+                {this.menuType === 'route' && 
+                    <>
+                        <IconButton
+                            tooltip="Delete Route" //
+                            onPointerDown={this.Delete}
+                            icon={<FontAwesomeIcon icon="trash-alt" />}
+                            color={SettingsManager.userColor}
+                        />
                         <IconButton
                             tooltip='Animate route'
-                            onPointerDown={this.Delete} /**TODO: fix */
+                            onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} /**TODO: fix */
                             icon={<FontAwesomeIcon icon={faRoute as IconLookup}/>}
                             color={SettingsManager.userColor}
                         />
+                        <div ref={this._commentRef}>
+                            <IconButton
+                                tooltip="Link Note to Pin" //
+                                onPointerDown={this.notePointerDown}
+                                icon={<FontAwesomeIcon icon="sticky-note" />}
+                                color={SettingsManager.userColor}
+                            />
+                        </div>
                         <IconButton
                             tooltip='Add to calendar'
                             onPointerDown={this.Delete} /**TODO: fix */
                             icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
                             color={SettingsManager.userColor}
                         />
+
                     </>
+
                 }
                 {this.menuType === 'customize' &&
                     <>
@@ -351,7 +403,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         />
                         <IconButton
                             tooltip="Revert to original" //
-                            onPointerDown={this.BackClick}
+                            onPointerDown={() => this.revertToOriginalMarker()}
                             icon={<FontAwesomeIcon icon={faArrowsRotate as IconLookup} />}
                             color={SettingsManager.userColor}
                         />
@@ -386,7 +438,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                 {this.menuType === 'standard' &&
                     <div>{this.title}</div>
                 }
-                {this.menuType === 'route' && 
+                {this.menuType === 'routeCreation' && 
                     <div className='direction-inputs' style={{display: 'flex', flexDirection: 'column'}}>
                         <TextField
                             fullWidth
@@ -493,27 +545,27 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                             circleSize={15}
                             circleSpacing={7}
                             width='100%'
-                            onChange={(color) => console.log(color.hex)}
+                            onChange={(color) => this.onMarkerColorChange(color)}
                         />
                     </div>
                     <div className='all-markers-container'>
-                         {Object.keys(MarkerIcons.FAMarkerIconsMap).map((iconKey) => {
-                            const icon = MarkerIcons.getFontAwesomeIcon(iconKey);
-                            if (icon){
-                                return (
-                                    <div key={iconKey} className='marker-icon'>
-                                        <IconButton
-                                            onPointerDown={() => {}}
-                                            icon={MarkerIcons.getFontAwesomeIcon(iconKey, 'white')}
-                                        />
-                                    </div>
-                                )
-                            }
-                            return null;
-                        })}
+                         {Object.keys(MarkerIcons.FAMarkerIconsMap).map((iconKey) => (
+                            <div key={iconKey} className='marker-icon'>
+                                <IconButton
+                                    onPointerDown={() => this.onMarkerIconChange(iconKey)}
+                                    icon={MarkerIcons.getFontAwesomeIcon(iconKey, '1x', 'white')}
+                                />
+                            </div>
+                        ))}
                     </div>
                     <div style={{width: '100%', height:'3px', color: 'white'}}></div>
                 </div>
+                }
+                {this.menuType === 'route' && this.routeDoc &&
+                    <div>
+                        {StrCast(this.routeDoc.title)}
+                    </div>
+                
                 }
                 {buttons}
             </div>
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index bc2f90fbd..d3c6bb14e 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -82,6 +82,56 @@
 
     }
 
+    .animation-panel {
+        z-index: 900;
+        display: flex;
+        flex-direction: column;
+        justify-content: flex-start;
+        align-items: flex-start;
+        position: absolute;
+        background-color: rgb(187, 187, 187);
+        padding: 10px;
+        border-top-right-radius: 5px;
+        border-bottom-right-radius: 5px;
+        width: 100%;
+
+        #route-to-animate-title {
+            font-size: 1.25em;
+            font-weight: bold;
+        }
+
+        .route-animation-options {
+            display: flex;
+            justify-content: flex-start;
+            align-items: center;
+            gap: 7px;
+
+            .animation-suboptions{
+                display: flex;
+                justify-content: flex-start;
+                align-items: center;
+                gap: 7px;
+
+                label{
+                    margin-bottom: 0;
+                }
+    
+                .speed-label{
+                    margin-right: 5px;
+                }
+
+                #last-divider{
+                    margin-left: 10px;
+                    margin-right: 10px;
+                }
+            }
+
+            
+        }
+
+    }
+
+
     .mapBox-topbar {
         display: flex;
         flex-direction: row;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index ac926e1fb..cde68a2e6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import BingMapsReact from 'bingmaps-react';
 // import 'mapbox-gl/dist/mapbox-gl.css';
 
-import { Button, EditableText, IconButton, Type } from 'browndash-components';
+import { Button, EditableText, IconButton, Size, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS} from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -51,14 +51,20 @@ import debounce from 'debounce';
 import './MapBox.scss';
 import { NumberLiteralType } from 'typescript';
 // import { GeocoderControl } from './GeocoderControl';
-import mapboxgl, { LngLat, MapLayerMouseEvent } from 'mapbox-gl';
+import mapboxgl, { LngLat, LngLatBoundsLike, MapLayerMouseEvent, MercatorCoordinate } from 'mapbox-gl';
 import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, MultiLineString, Position } from 'geojson';
 import { MarkerEvent } from 'react-map-gl/dist/esm/types';
 import { MapboxApiUtility, TransportationType} from './MapboxApiUtility';
 import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
-import { IconLookup, faGear } from '@fortawesome/free-solid-svg-icons';
+import { IconLookup, faCircleXmark, faFileExport, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
+import { MarkerIcons } from './MarkerIcons';
+import { SettingsManager } from '../../../util/SettingsManager';
+import * as turf from '@turf/turf';
+import * as d3 from "d3";
+import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility';
+import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons';
 
 // amongus
 /**
@@ -142,25 +148,93 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allRoutes() {
         return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE);
     }
+    @computed get updatedRouteCoordinates(): Feature<Geometry, GeoJsonProperties> {
+        if (this.routeToAnimate?.routeCoordinates) {
+            const originalCoordinates: Position[] = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
+            // const index = Math.floor(this.animationPhase * originalCoordinates.length);
+            const index = this.animationPhase * (originalCoordinates.length - 1); // Calculate the fractional index
+            const startIndex = Math.floor(index);
+            const endIndex = Math.ceil(index);
+
+            if (startIndex === endIndex) {
+                // AnimationPhase is at a whole number (no interpolation needed)
+                const coordinates = [originalCoordinates[startIndex]];
+                const geometry: LineString = {
+                    type: 'LineString',
+                    coordinates,
+                };
+                return {
+                    type: 'Feature',
+                    properties: {
+                        'routeTitle': StrCast(this.routeToAnimate.title)
+                    },
+                    geometry: geometry,
+                };
+            } else {
+                // Interpolate between two coordinates
+                const startCoord = originalCoordinates[startIndex];
+                const endCoord = originalCoordinates[endIndex];
+                const fraction = index - startIndex;
+                
+                // Interpolate the coordinates
+                const interpolatedCoord = [
+                    startCoord[0] + fraction * (endCoord[0] - startCoord[0]),
+                    startCoord[1] + fraction * (endCoord[1] - startCoord[1]),
+                ];
+    
+                const coordinates = originalCoordinates.slice(0, startIndex + 1).concat([interpolatedCoord]);
+                
+                const geometry: LineString = {
+                    type: 'LineString',
+                    coordinates,
+                };
+                return {
+                    type: 'Feature',
+                    properties: {
+                        'routeTitle': StrCast(this.routeToAnimate.title)
+                    },
+                    geometry: geometry,
+                };
+            }
+        }
+        return {
+            type: 'Feature',
+            properties: {},
+            geometry: {
+                type: 'LineString',
+                coordinates: []
+            },
+        };
+    }
+    @computed get selectedRouteCoordinates(): Position[] {
+        let coordinates: Position[] = [];
+        if (this.routeToAnimate?.routeCoordinates){
+            coordinates = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
+        }
+        return coordinates;
+    }
+
     @computed get allRoutesGeoJson(): FeatureCollection {
-        const features: Feature<Geometry, GeoJsonProperties>[] = this.allRoutes.map(route => {
-            console.log("Route coords: ", route.coordinates);
+        const features: Feature<Geometry, GeoJsonProperties>[] = this.allRoutes.map((routeDoc: Doc) => {
+            console.log('Route coords: ', routeDoc.routeCoordinates);
             const geometry: LineString = {
                 type: 'LineString',
-                coordinates: JSON.parse(StrCast(route.coordinates))
-            }
+                coordinates: JSON.parse(StrCast(routeDoc.routeCoordinates)),
+            };
             return {
-              type: 'Feature',
-              properties: {},
-              geometry: geometry
+                type: 'Feature',
+                properties: {
+                    'routeTitle': routeDoc.title},
+                geometry: geometry,
             };
-          });
-        
-          return {
+        });
+
+        return {
             type: 'FeatureCollection',
-            features: features
-          };
+            features: features,
+        };
     }
+
     @computed get SidebarShown() {
         return this.layoutDoc._layout_showSidebar ? true : false;
     }
@@ -184,7 +258,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     _unmounting = false;
     componentWillUnmount(): void {
         this._unmounting = true;
-        this.deselectPin();
+        this.deselectPinOrRoute();
         this._rerenderTimeout && clearTimeout(this._rerenderTimeout);
         Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
     }
@@ -199,7 +273,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
-            let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin;
+            let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPinOrRoute;
             if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) {
                 existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map));
             }
@@ -300,10 +374,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         const sourceAnchorCreator = action(() => {
             const note = this.getAnchor(true);
-            if (note && this.selectedPin) {
-                note.latitude = this.selectedPin.latitude;
-                note.longitude = this.selectedPin.longitude;
-                note.map = this.selectedPin.map;
+            if (note && this.selectedPinOrRoute) {
+                note.latitude = this.selectedPinOrRoute.latitude;
+                note.longitude = this.selectedPinOrRoute.longitude;
+                note.map = this.selectedPinOrRoute.map;
             }
             return note as Doc;
         });
@@ -329,10 +403,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const createFunc = undoable(
             action(() => {
                 const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]);
-                if (note && this.selectedPin) {
-                    note.latitude = this.selectedPin.latitude;
-                    note.longitude = this.selectedPin.longitude;
-                    note.map = this.selectedPin.map;
+                if (note && this.selectedPinOrRoute) {
+                    note.latitude = this.selectedPinOrRoute.latitude;
+                    note.longitude = this.selectedPinOrRoute.longitude;
+                    note.map = this.selectedPinOrRoute.map;
                 }
             }),
             'create note annotation'
@@ -410,11 +484,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     // The pin that is selected
-    @observable selectedPin: Doc | undefined;
+    @observable selectedPinOrRoute: Doc | undefined;
+
 
     @action
-    deselectPin = () => {
-        if (this.selectedPin) {
+    deselectPinOrRoute = () => {
+        if (this.selectedPinOrRoute) {
             // // Removes filter
             // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
@@ -435,6 +510,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
 
+
     getView = async (doc: Doc) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar();
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
@@ -444,22 +520,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     @action
     pushpinClicked = (pinDoc: Doc) => {
-        this.deselectPin();
-        this.selectedPin = pinDoc;
+        this.deselectPinOrRoute();
+        this.selectedPinOrRoute = pinDoc;
         this.bingSearchBarContents = pinDoc.map;
 
         // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
         // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
-        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
+        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
 
-        this.recolorPin(this.selectedPin, 'green');
+        this.recolorPin(this.selectedPinOrRoute, 'green');
 
-        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
+        MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
         MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
         MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
         MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
 
-        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
+        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPinOrRoute.latitude, this.selectedPinOrRoute.longitude));
         const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
         const y = point.y + this.props.PanelHeight() / 2 + 32;
         const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
@@ -474,7 +550,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
         this.props.select(false);
-        this.deselectPin();
+        this.deselectPinOrRoute();
     };
     /*
      * Updates values of layout doc to match the current map
@@ -520,14 +596,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.rootDoc.title,
-            text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
-            config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
-            config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
+            text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.rootDoc.map) || 'map location',
+            config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude),
+            config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
             config_map_type: StrCast(this.dataDoc.map_type),
-            config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map),
+            config_map: StrCast((existingPin ?? this.selectedPinOrRoute)?.map) || StrCast(this.dataDoc.map),
             layout_unrendered: true,
-            mapPin: existingPin ?? this.selectedPin,
+            mapPin: existingPin ?? this.selectedPinOrRoute,
             annotationOn: this.rootDoc,
         });
         if (anchor) {
@@ -566,7 +642,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      *   Removes pin from annotations
      */
     @action
-    removePushpin = (pinDoc: Doc) => this.removeMapDocument(pinDoc, this.annotationKey);
+    removePushpinOrRoute = (pinOrRouteDoc: Doc) => this.removeMapDocument(pinOrRouteDoc, this.annotationKey);
 
     /*
      * Removes pushpin from map render
@@ -576,23 +652,25 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
         }
         this.map_docToPinMap.delete(pinDoc);
-        this.selectedPin = undefined;
+        this.selectedPinOrRoute = undefined;
     };
 
     @action
-    deleteSelectedPin = undoable(() => {
-        if (this.selectedPin) {
+    deleteSelectedPinOrRoute = undoable(() => {
+        if (this.selectedPinOrRoute) {
             // Removes filter
-            Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
-            Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
-            Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+            Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPinOrRoute.latitude, 'remove');
+            Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPinOrRoute.longitude, 'remove');
+            Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPinOrRoute))}`, 'remove');
 
-            this.removePushpin(this.selectedPin);
+            this.removePushpinOrRoute(this.selectedPinOrRoute);
         }
         MapAnchorMenu.Instance.fadeOut(true);
         document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
     }, 'delete pin');
 
+
+
     tryHideMapAnchorMenu = (e: PointerEvent) => {
         let target = document.elementFromPoint(e.x, e.y);
         while (target) {
@@ -608,9 +686,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     centerOnSelectedPin = () => {
-        if (this.selectedPin) {
+        if (this.selectedPinOrRoute) {
             this._mapRef.current?.flyTo({
-                center: [NumCast(this.selectedPin.longitude), NumCast(this.selectedPin.latitude)]
+                center: [NumCast(this.selectedPinOrRoute.longitude), NumCast(this.selectedPinOrRoute.latitude)]
             })
         }
         // if (this.selectedPin) {
@@ -776,12 +854,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             false,
             [],
             {
-                title: location ?? `lat=${latitude},lng=${longitude}`, 
+                title: location ?? `lat=${NumCast(latitude)},lng=${NumCast(longitude)}`, 
                 map: location, 
                 description: "", 
                 wikiData: wikiData,
-                markerType: 'MAPBOX_MARKER',
-                markerColor: '#3FB1CE'
+                markerType: 'MAP_PIN',
+                markerColor: '#ff5722'
             },
             // { title: map ?? `lat=${latitude},lng=${longitude}`, map: map },
             // ,'pushpinIDamongus'+ this.incrementer++
@@ -794,14 +872,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }, 'createpin');
 
     @action
-    createMapRoute = undoable((coordinates: Position[], origin: string, destination: string) => {
-        console.log(coordinates);
+    createMapRoute = undoable((coordinates: Position[], origin: string, destination: any, createPinForDestination: boolean) => {
         const mapRoute = Docs.Create.MapRouteDocument(
             false,
             [],
-            {title: `${origin} -> ${destination}`, routeCoordinates: JSON.stringify(coordinates)},
+            {title: `${origin} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates)},
         );
         this.addDocument(mapRoute, this.annotationKey);
+        if (createPinForDestination) {
+            this.createPushpin(destination.center[1], destination.center[0], destination.place_name);
+        }
         return mapRoute;
 
         // mapMarker.infoWindowOpen = true;
@@ -853,10 +933,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     handleSearchChange = async (searchText: string) => {
         const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText);
-        if (features){
+        if (features && !this.isAnimating){
             runInAction(() => {
                 this.settingsOpen= false;
                 this.featuresFromGeocodeResults = features;
+                this.routeToAnimate = undefined;
             })
         }
         // try {
@@ -874,12 +955,65 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     // @action
     // debouncedCall = React.useCallback(debounce(this.debouncedOnSearchBarChange, 300), []);
 
+
+    @action
+    handleMapClick = (e: MapLayerMouseEvent) => {
+        if (this._mapRef.current){
+            const features = this._mapRef.current.queryRenderedFeatures(
+                e.point, {
+                    layers: ['map-routes-layer']
+                }
+            );
+
+            console.error(features);
+            if (features && features.length > 0 && features[0].properties && features[0].geometry) {
+                const geometry = features[0].geometry as LineString;
+                const routeTitle: string = features[0].properties['routeTitle'];
+                const routeDoc: Doc | undefined = this.allRoutes.find((routeDoc) => routeDoc.title === routeTitle);
+                this.deselectPinOrRoute(); // TODO: Also deselect route if selected
+                if (routeDoc){
+                    this.selectedPinOrRoute = routeDoc;
+                    Doc.setDocFilter(this.rootDoc, LinkedTo, `mapRoute=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
+
+                    // TODO: Recolor route 
+
+                    MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
+                    MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
+                    MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
+                    MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
+
+                    MapAnchorMenu.Instance.setRouteDoc(routeDoc);
+
+                    // TODO: Subject to change
+                    MapAnchorMenu.Instance.setAllMapboxPins(
+                        this.allAnnotations.filter(anno => !anno.layout_unrendered)
+                    )
+
+                    MapAnchorMenu.Instance.DisplayRoute = this.displayRoute;
+                    MapAnchorMenu.Instance.HideRoute = this.hideRoute;
+                    MapAnchorMenu.Instance.AddNewRouteToMap = this.createMapRoute;
+                    MapAnchorMenu.Instance.CreatePin = this.addMarkerForFeature;
+                    MapAnchorMenu.Instance.OpenAnimationPanel = this.openAnimationPanel;
+
+                    // this.selectedRouteCoordinates = geometry.coordinates;
+
+                    MapAnchorMenu.Instance.setMenuType('route');
+
+                    MapAnchorMenu.Instance.jumpTo(e.originalEvent.clientX, e.originalEvent.clientY, true);
+
+                    document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+                }        
+            }
+        }
+    }
+
+
     /**
      * Makes a reverse geocoding API call to retrieve features corresponding to a map click (based on longitude
      * and latitude). Sets the search results accordingly. 
      * @param e 
      */
-    handleMapClick = async (e: MapLayerMouseEvent) => {
+    handleMapDblClick = async (e: MapLayerMouseEvent) => {
         e.preventDefault();
         const lngLat: LngLat = e.lngLat;
         const longitude: number = lngLat.lng;
@@ -914,18 +1048,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     handleMarkerClick = (e: MarkerEvent<mapboxgl.Marker, MouseEvent>, pinDoc: Doc) => {
         this.featuresFromGeocodeResults = [];
-        this.deselectPin(); // TODO: check this method
-        this.selectedPin = pinDoc;
+        this.deselectPinOrRoute(); // TODO: check this method
+        this.selectedPinOrRoute = pinDoc;
         // this.bingSearchBarContents = pinDoc.map;
 
         // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
         // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
-        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
+        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
 
-        this.recolorPin(this.selectedPin, 'green'); // TODO: check this method
+        this.recolorPin(this.selectedPinOrRoute, 'green'); // TODO: check this method
        
         
-        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
+        MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
         MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
         MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
         MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
@@ -941,11 +1075,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         MapAnchorMenu.Instance.AddNewRouteToMap = this.createMapRoute;
         MapAnchorMenu.Instance.CreatePin = this.addMarkerForFeature;
 
-        // const longitude = NumCast(pinDoc.longitude);
-        // const latitude = NumCast(pinDoc.longitude);
-        // const x = longitude + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
-        // const y = latitude + this.props.PanelHeight() / 2 + 20;
-        // const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
+        MapAnchorMenu.Instance.setMenuType('standard');
+
         MapAnchorMenu.Instance.jumpTo(e.originalEvent.clientX, e.originalEvent.clientY, true);
 
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
@@ -979,6 +1110,351 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     }
 
+    @observable
+    isAnimating: boolean = false;
+
+    @observable
+    isPaused: boolean = false;
+
+    @observable
+    routeToAnimate: Doc | undefined = undefined;
+
+    @observable 
+    animationPhase: number = 0;
+
+    @observable 
+    finishedFlyTo: boolean = false;
+
+    @action
+    setAnimationPhase = (newValue: number) => {
+        this.animationPhase = newValue;
+    };
+
+    @observable 
+    frameId: number | null = null;
+
+    @action
+    setFrameId = (frameId: number) => {
+        this.frameId = frameId;
+    }
+
+    @action
+    openAnimationPanel = (routeDoc: Doc | undefined) => {
+        if (routeDoc){
+            MapAnchorMenu.Instance.fadeOut(true);
+            document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
+            this.routeToAnimate = routeDoc;
+        }
+    }
+
+    @observable
+    animationDuration = 40000;
+
+    @observable
+    animationAltitude = 12800;
+
+    @observable
+    pathDistance = 0;
+
+    @observable 
+    isStreetViewAnimation: boolean = false;
+
+    @observable 
+    animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM;
+
+    @action
+    updateAnimationSpeed = () => {
+        switch (this.animationSpeed){
+            case AnimationSpeed.SLOW:
+                this.animationSpeed = AnimationSpeed.MEDIUM;
+                break;
+            case AnimationSpeed.MEDIUM:
+                this.animationSpeed = AnimationSpeed.FAST;
+                break;
+            case AnimationSpeed.FAST:
+                this.animationSpeed = AnimationSpeed.SLOW;
+                break;
+            default:
+                this.animationSpeed = AnimationSpeed.MEDIUM;
+                break;
+        }
+    }
+    @computed get animationSpeedTooltipText(): string {
+        switch (this.animationSpeed) {
+          case AnimationSpeed.SLOW:
+            return '1x speed';
+          case AnimationSpeed.MEDIUM:
+            return '2x speed';
+          case AnimationSpeed.FAST:
+            return '3x speed';
+          default:
+            return '2x speed'; 
+        }
+    }
+    @computed get animationSpeedIcon(): JSX.Element{
+        switch (this.animationSpeed) {
+            case AnimationSpeed.SLOW:
+              return slowSpeedIcon;
+            case AnimationSpeed.MEDIUM:
+              return mediumSpeedIcon;
+            case AnimationSpeed.FAST:
+              return fastSpeedIcon;
+            default:
+              return mediumSpeedIcon; 
+          }
+    }
+
+    @action
+    toggleIsStreetViewAnimation = () => {
+        this.isStreetViewAnimation = !this.isStreetViewAnimation;
+    }
+
+    @observable 
+    dynamicRouteFeature: Feature<Geometry, GeoJsonProperties> = {
+        type: 'Feature',
+        properties: {},
+        geometry: {
+            type: 'LineString',
+            coordinates: []
+        }
+    };
+
+
+    @observable 
+    path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties> = {
+        type: 'Feature',
+        geometry: {
+            type: 'LineString',
+            coordinates: []
+        },
+        properties: {}
+    };
+
+    getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => {
+        const geometry: LineString = {
+            type: 'LineString',
+            coordinates: JSON.parse(StrCast(routeDoc.routeCoordinates)),
+        };
+        return {
+            type: 'Feature',
+            properties: {
+                'routeTitle': routeDoc.title},
+            geometry: geometry,
+        };
+    }
+
+    @action 
+    playAnimation = (status: AnimationStatus) => {
+        if (!this._mapRef.current || !this.routeToAnimate){
+            return;
+        }
+
+        if (this.isAnimating){
+            return;
+        }
+        this.animationPhase = status === AnimationStatus.RESUME ? this.animationPhase : 0;
+        this.frameId = AnimationStatus.RESUME ? this.frameId : null;
+        this.finishedFlyTo = AnimationStatus.RESUME ? this.finishedFlyTo : false;
+
+        const path = turf.lineString(this.selectedRouteCoordinates);
+        
+        this.settingsOpen = false;
+        this.path = path;
+        this.pathDistance = turf.lineDistance(path);
+        this.isAnimating = true;
+        runInAction(() => {
+            return new Promise<void>(async (resolve) => {
+                let animationUtil;
+                try {
+                const targetLngLat = {
+                    lng: this.selectedRouteCoordinates[0][0],
+                    lat: this.selectedRouteCoordinates[0][1],
+                };
+
+                animationUtil = new AnimationUtility(
+                    targetLngLat,
+                    this.selectedRouteCoordinates,
+                    this.isStreetViewAnimation,
+                    this.animationSpeed
+                );
+
+
+                const updateFrameId = (newFrameId: number) => {
+                    this.setFrameId(newFrameId);
+                }
+
+                const updateAnimationPhase = (
+                    newAnimationPhase: number,
+                    ) => {
+                    this.setAnimationPhase(newAnimationPhase);
+                };
+                
+                if (status !== AnimationStatus.RESUME) {
+
+                    const result = await animationUtil.flyInAndRotate({
+                        map: this._mapRef.current!,
+                        // targetLngLat,
+                        // duration 3000
+                        // startAltitude: 3000000,
+                        // endAltitude: this.isStreetViewAnimation ? 80 : 12000,
+                        // startBearing: 0,
+                        // endBearing: -20,
+                        // startPitch: 40,
+                        // endPitch: this.isStreetViewAnimation ? 80 : 50,
+                        updateFrameId,
+                    });
+
+                    console.log("Bearing: ", result.bearing);
+                    console.log("Altitude: ", result.altitude);
+
+                } 
+
+                runInAction(() => {
+                    this.finishedFlyTo = true;
+                })
+
+                // follow the path while slowly rotating the camera, passing in the camera bearing and altitude from the previous animation
+                await animationUtil.animatePath({
+                    map: this._mapRef.current!, 
+                    // path: this.path,
+                    // startBearing: -20,
+                    // startAltitude: this.isStreetViewAnimation ? 80 : 12000,
+                    // pitch: this.isStreetViewAnimation ? 80: 50,
+                    currentAnimationPhase: this.animationPhase,
+                    updateAnimationPhase,
+                    updateFrameId,
+                });
+            
+                // get the bounds of the linestring, use fitBounds() to animate to a final view
+                const bbox3d = turf.bbox(this.path);
+    
+                const bbox2d: LngLatBoundsLike = [bbox3d[0], bbox3d[1], bbox3d[2], bbox3d[3]];
+    
+                this._mapRef.current!.fitBounds(bbox2d, {
+                    duration: 3000,
+                    pitch: 30,
+                    bearing: 0,
+                    padding: 120,
+                });
+            
+                setTimeout(() => {
+                    resolve();
+                }, 10000);
+                } catch (error: any){
+                    console.log(error);
+                    console.log('animation util: ', animationUtil);
+                }});
+            
+            })
+            
+
+    }
+
+
+    @action 
+    pauseAnimation = () => {
+        if (this.frameId && this.animationPhase > 0){
+            window.cancelAnimationFrame(this.frameId);
+            this.frameId = null;
+            this.isAnimating = false;
+        }
+    }
+
+    @action
+    stopAndCloseAnimation = () => {
+        if (this.frameId){
+            window.cancelAnimationFrame(this.frameId);
+            this.frameId = null;
+            this.finishedFlyTo = false;
+            this.isAnimating = false;
+            this.animationPhase = 0;
+            this.routeToAnimate = undefined;
+            // this.selectedRouteCoordinates = [];
+        }
+        // reset bearing and pitch to original, zoom out  
+    }
+
+    @action
+    exportAnimationToVideo = () => {
+
+    }
+
+    getRouteAnimationOptions = (): JSX.Element => {
+        return (
+            <>
+                <IconButton 
+                    tooltip={this.isAnimating && this.finishedFlyTo ? 'Pause Animation' : 'Play Animation'}
+                    onPointerDown={() => {
+                        if (this.isAnimating && this.finishedFlyTo) {
+                            this.pauseAnimation();
+                        } else if (this.animationPhase > 0) {
+                            this.playAnimation(AnimationStatus.RESUME); // Resume from the current phase
+                        } else {
+                            this.playAnimation(AnimationStatus.START); // Play from the beginning
+                        }
+                    }}
+                    icon={this.isAnimating && this.finishedFlyTo ? 
+                        <FontAwesomeIcon icon={faPause as IconLookup}/>  
+                        :
+                        <FontAwesomeIcon icon={faPlay as IconLookup}/>    
+                    }
+                    color='black'
+                    size={Size.MEDIUM}
+                />
+                {this.isAnimating && this.finishedFlyTo && 
+                <IconButton 
+                    tooltip='Restart animation'
+                    onPointerDown={() => this.playAnimation(AnimationStatus.RESTART)}
+                    icon={<FontAwesomeIcon icon={faRotate as IconLookup}/>}
+                    color='black'
+                    size={Size.MEDIUM}
+                />
+                    
+                }
+                <IconButton 
+                    tooltip='Stop and close animation'
+                    onPointerDown={this.stopAndCloseAnimation}
+                    icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup}/>}
+                    color='black'
+                    size={Size.MEDIUM}
+                />
+                <IconButton 
+                    style={{marginRight: '10px'}}
+                    tooltip='Export to video'
+                    onPointerDown={this.exportAnimationToVideo}
+                    icon={<FontAwesomeIcon icon={faFileExport as IconLookup}/>}
+                    color='black'
+                    size={Size.MEDIUM}
+                />
+                {!this.isAnimating &&
+                    <>
+                        <div className='animation-suboptions'>
+                            <div>|</div>
+                            <FormControlLabel
+                                label='Street view animation'
+                                labelPlacement='start'
+                                control={
+                                    <Checkbox 
+                                        color='success'
+                                        checked={this.isStreetViewAnimation}
+                                        onChange={this.toggleIsStreetViewAnimation}
+                                    />
+                                }
+                            />
+                            <div id='last-divider'>|</div>
+                            <IconButton
+                                tooltip={this.animationSpeedTooltipText}
+                                onPointerDown={this.updateAnimationSpeed}
+                                icon={this.animationSpeedIcon}
+                                size={Size.MEDIUM}
+                            />
+                        </div>
+                    </>
+                }
+            </>
+        )
+    }
+
     @action
     hideRoute = () => {
         this.temporaryRouteSource = {
@@ -1015,8 +1491,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     toggleSettings = () => {
-        this.featuresFromGeocodeResults = [];
-        this.settingsOpen = !this.settingsOpen;
+        if (!this.isAnimating && this.animationPhase == 0) {
+            this.featuresFromGeocodeResults = [];
+            this.settingsOpen = !this.settingsOpen;
+        }
     }
 
     @action
@@ -1056,6 +1534,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     }
 
+    getMarkerIcon = (pinDoc: Doc): JSX.Element | null => {
+        const markerType = StrCast(pinDoc.markerType);
+        const markerColor = StrCast(pinDoc.markerColor);
+
+        return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor) ?? null;
+        
+    }
+
+
+
 
 
 
@@ -1092,21 +1580,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+                    {!this.routeToAnimate &&
+                        <div className="mapBox-searchbar">
+                            <TextField 
+                                fullWidth
+                                placeholder='Enter a location'
+                                onChange={(e) => this.handleSearchChange(e.target.value)}
+                            />
+                            <IconButton
+                                icon={<FontAwesomeIcon icon={faGear as IconLookup} size='1x'/>}
+                                type={Type.TERT}
+                                onClick={(e) => this.toggleSettings()}
 
-                    <div className="mapBox-searchbar">
-                        <TextField 
-                            fullWidth
-                            placeholder='Enter a location'
-                            onChange={(e) => this.handleSearchChange(e.target.value)}
-                        />
-                        <IconButton
-                            icon={<FontAwesomeIcon icon={faGear as IconLookup} size='1x'/>}
-                            type={Type.TERT}
-                            onClick={(e) => this.toggleSettings()}
-
-                        />
-                    </div> 
-                    {this.settingsOpen &&
+                            />
+                        </div> 
+                    }
+                    {this.settingsOpen && !this.routeToAnimate && 
                         <div className='mapbox-settings-panel' style={{right: `${0+ this.sidebarWidth()}px`}}>
                             <div className='mapbox-style-select'>
                                 <div>
@@ -1151,45 +1640,52 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             </div>
                         </div>
                     }
-                
-                    <div className='mapbox-geocoding-search-results'>
-                        {this.featuresFromGeocodeResults.length > 0 && (
+                    {this.routeToAnimate &&
+                        <div className='animation-panel'>
+                            <div id='route-to-animate-title'>
+                                {StrCast(this.routeToAnimate.title)}
+                            </div>
+                            <div className='route-animation-options'>
+                                {this.getRouteAnimationOptions()}
+                            </div>
+                        </div>
+                    }
+                    {this.featuresFromGeocodeResults.length > 0 && (
+                        <div className='mapbox-geocoding-search-results'>
                             <React.Fragment>
-                            <h4>Choose a location for your pin: </h4>
-                            {this.featuresFromGeocodeResults
-                                .filter(feature => feature.place_name)
-                                .map((feature, idx) => (
-                                <div 
-                                    key={idx} 
-                                    className='search-result-container'
-                                    onClick={() => {
-                                        this.handleSearchChange("");
-                                        this.addMarkerForFeature(feature);
-                                    }}
-                                >
-                                    <div className='search-result-place-name'> 
-                                    {feature.place_name}
+                                <h4>Choose a location for your pin: </h4>
+                                {this.featuresFromGeocodeResults
+                                    .filter(feature => feature.place_name)
+                                    .map((feature, idx) => (
+                                    <div 
+                                        key={idx} 
+                                        className='search-result-container'
+                                        onClick={() => {
+                                            this.handleSearchChange("");
+                                            this.addMarkerForFeature(feature);
+                                        }}
+                                    >
+                                        <div className='search-result-place-name'> 
+                                        {feature.place_name}
+                                        </div>
                                     </div>
-                                </div>
                                 ))}
                             </React.Fragment>
-                        )}
-                    </div>              
+                            
+                        </div>   
+                    )}           
                     <MapProvider>
                         <MapboxMap
                             ref={this._mapRef}
-                            initialViewState={{
-                                longitude: -100,
-                                latitude: 40,
-                                zoom: 3.5
-                              }}
                             mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
                             id="mapbox-map"
                             mapStyle={this.mapStyle}
                             style={{height: '100%', width: '100%'}}
-                            {...this.mapboxMapViewState}
+                            initialViewState={this.isAnimating ? undefined : this.mapboxMapViewState}
+                            // {...this.mapboxMapViewState}
                             onMove={this.onMapMove}
-                            onDblClick={this.handleMapClick}
+                            onClick={this.handleMapClick}
+                            onDblClick={this.handleMapDblClick}
                             terrain={this.showTerrain ? { source: 'mapbox-dem', exaggeration: 2.0 } : undefined}
                             
                             
@@ -1210,6 +1706,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 layout={{"line-join": "round", "line-cap": "round"}}
                                 paint={{"line-color": "#36454F", "line-width": 4, "line-dasharray": [1,1]}}
                                 />
+                            {!this.isAnimating && this.animationPhase == 0 &&
                             <Layer
                                 id='map-routes-layer'
                                 type='line'
@@ -1217,9 +1714,60 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 layout={{"line-join": "round", "line-cap": "round"}}
                                 paint={{"line-color": "#FF0000", "line-width": 4}}
                                 />
+                            }
+                            {this.routeToAnimate && (this.isAnimating || this.animationPhase > 0) &&
+                                <>
+                                    {!this.isStreetViewAnimation &&
+                                        <>
+                                            <Source id='animated-route' type='geojson' data={this.updatedRouteCoordinates}/>
+                                            <Layer 
+                                            id='dynamic-animation-line' 
+                                            type='line' 
+                                            source='animated-route'
+                                            paint={{
+                                                'line-color': 'yellow',
+                                                'line-width': 4,
+                                            }}
+                                            />
+                                        </>
+                                        }
+                                    <Source id='start-pin-base' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.04)}/>
+                                    <Source id='start-pin-top' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.25)}/>
+                                    <Source id='end-pin-base' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.04)}/>
+                                    <Source id='end-pin-top' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.25)}/>
+                                    <Layer id='start-fill-pin-base' type='fill-extrusion' source='start-pin-base'
+                                        paint={{
+                                            'fill-extrusion-color': '#0bfc03',
+                                            'fill-extrusion-height': 1000
+                                        }}
+                                    />
+                                    <Layer id='start-fill-pin-top' type='fill-extrusion' source='start-pin-top'
+                                        paint={{
+                                            'fill-extrusion-color': '#0bfc03',
+                                            'fill-extrusion-base': 1000,
+                                            'fill-extrusion-height': 1200
+                                        }}
+                                    />
+                                    <Layer id='end-fill-pin-base' type='fill-extrusion' source='end-pin-base'
+                                        paint={{
+                                            'fill-extrusion-color': '#eb1c1c',
+                                            'fill-extrusion-height': 1000
+                                        }}
+                                    />
+                                    <Layer id='end-fill-pin-top' type='fill-extrusion' source='end-pin-top'
+                                        paint={{
+                                            'fill-extrusion-color': '#eb1c1c',
+                                            'fill-extrusion-base': 1000,
+                                            'fill-extrusion-height': 1200
+                                        }}
+                                    />
+                                    
+                                </>
+                            }
+                            
 
                             <>
-                                {this.allPushpins
+                                {!this.isAnimating && this.animationPhase == 0 && this.allPushpins
                                     // .filter(anno => !anno.layout_unrendered)
                                     .map((pushpin, idx) => (
                                         <Marker 
@@ -1228,7 +1776,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                             latitude={NumCast(pushpin.latitude)}
                                             anchor='bottom'
                                             onClick={(e: MarkerEvent<mapboxgl.Marker, MouseEvent>) => this.handleMarkerClick(e, pushpin)}
-                                        />
+                                        >
+                                            {this.getMarkerIcon(pushpin)}
+                                        </Marker>
                                 ))}
                             </>
                             
diff --git a/src/client/views/nodes/MapBox/MarkerIcons.tsx b/src/client/views/nodes/MapBox/MarkerIcons.tsx
index cf50109ac..146f296c1 100644
--- a/src/client/views/nodes/MapBox/MarkerIcons.tsx
+++ b/src/client/views/nodes/MapBox/MarkerIcons.tsx
@@ -1,57 +1,46 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { faShopify } from '@fortawesome/free-brands-svg-icons';
-import { IconLookup, faBasketball, faBicycle, faBowlFood, faBus, faCameraRetro, faCar, faCartShopping, faFilm, faFootball, faFutbol, faHockeyPuck, faHospital, faHotel, faHouse, faLandmark, faMasksTheater, faMugSaucer, faPersonHiking, faPlane, faSchool, faShirt, faShop, faSquareParking, faStar, faTrainSubway, faTree, faUtensils, faVolleyball } from '@fortawesome/free-solid-svg-icons';
+import { faBasketball, faBicycle, faBowlFood, faBus, faCameraRetro, faCar, faCartShopping, faFilm, faFootball, faFutbol, faHockeyPuck, faHospital, faHotel, faHouse, faLandmark, faLocationDot, faLocationPin, faMapPin, faMasksTheater, faMugSaucer, faPersonHiking, faPlane, faSchool, faShirt, faShop, faSquareParking, faStar, faTrainSubway, faTree, faUtensils, faVolleyball } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import React = require('react');
 
-export type MapboxColor = 'yellow' | 'red' | 'orange' | 'purple' | 'pink' | 'blue' | 'green';
-type ColorProperties = {
-    fill: string,
-    stroke: string
-}
-type ColorsMap = {
-    [key in MapboxColor]: ColorProperties;
-}
-
 export class MarkerIcons {
 
-    static getMapboxIcon = (color: string) => {
-        return (
-            <svg xmlns="http://www.w3.org/2000/svg" id="marker" data-name="marker" width="20" height="48" viewBox="0 0 20 35">
-            <g id="mapbox-marker-icon">
-                <g id="icon">
-                <ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" />
-                <g id="mask" opacity="0.3">
-                    <g id="group">
-                    <path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fillRule="evenodd"/>
-                    </g>
-                </g>
-                <path id="color" fill={color} strokeWidth="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
-                <path id="circle" fill="#fff" stroke='white' strokeWidth="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
-                </g>
-            </g>
-            <rect width="20" height="48" fill="none"/>
-        </svg>
-        )
-    }
+    // static getMapboxIcon = (color: string) => {
+    //     return (
+    //         <svg xmlns="http://www.w3.org/2000/svg" id="marker" data-name="marker" width="20" height="48" viewBox="0 0 20 35">
+    //         <g id="mapbox-marker-icon">
+    //             <g id="icon">
+    //             <ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" />
+    //             <g id="mask" opacity="0.3">
+    //                 <g id="group">
+    //                 <path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fillRule="evenodd"/>
+    //                 </g>
+    //             </g>
+    //             <path id="color" fill={color} strokeWidth="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
+    //             <path id="circle" fill="#fff" stroke='white' strokeWidth="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
+    //             </g>
+    //         </g>
+    //         <rect width="20" height="48" fill="none"/>
+    //     </svg>
+    //     )
+    // }
 
-    static getFontAwesomeIcon(key: string, color?: string): JSX.Element | undefined {
+    static getFontAwesomeIcon(key: string, size: string, color?: string): JSX.Element {
         const icon: IconProp = MarkerIcons.FAMarkerIconsMap[key];
+        const iconProps: any = { icon };
 
-        if (icon) {
-            const iconProps: any = { icon };
-        
-            if (color) {
-              iconProps.color = color;
-            }
-        
-            return (<FontAwesomeIcon {...iconProps} size='1x' />);
-          }
+        if (color) {
+            iconProps.color = color;
+        }
+
+        return (<FontAwesomeIcon {...iconProps} size={size} />);
+          
         
-        return undefined;
     }
 
     static FAMarkerIconsMap: {[key: string]: IconProp} = {
+        'MAP_PIN': faLocationDot,
         'RESTAURANT_ICON': faUtensils,
         'HOTEL_ICON': faHotel,
         'HOUSE_ICON': faHouse,
-- 
cgit v1.2.3-70-g09d2

