From 5f523ba944c6b3f36248380352f32197106bf784 Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 12 Jan 2022 13:40:38 +0100 Subject: [PATCH 1/8] feat(itinerary-body): add mapillary image link to walking directions --- .storybook/preview.js | 5 +- .../src/AccessLegBody/access-leg-steps.js | 24 ++++- .../itinerary-body/src/AccessLegBody/index.js | 22 ++++- .../src/AccessLegBody/mapillary-button.tsx | 93 +++++++++++++++++++ .../itinerary-body/src/ItineraryBody/index.js | 13 +++ .../src/ItineraryBody/place-row.js | 8 ++ .../itinerary-body/src/__mocks__/handlers.js | 10 ++ .../src/__mocks__/mapillary.json | 11 +++ .../itinerary-body-defaults-wrapper.js | 1 + 9 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx create mode 100644 packages/itinerary-body/src/__mocks__/handlers.js create mode 100644 packages/itinerary-body/src/__mocks__/mapillary.json diff --git a/.storybook/preview.js b/.storybook/preview.js index 6bb6031bd..172805aed 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,9 +1,10 @@ import { setupWorker } from "msw"; -import handlers from "../packages/location-field/src/mocks/handlers"; +import locationFieldHandlers from "../packages/location-field/src/mocks/handlers"; +import itineraryBodyHandlers from '../packages/itinerary-body/src/__mocks__/handlers' // Only install worker when running in browser if (typeof global.process === 'undefined') { - const worker = setupWorker(...handlers); + const worker = setupWorker(...locationFieldHandlers, ...itineraryBodyHandlers) worker.start({onUnhandledRequest: "bypass"}) } diff --git a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js index ad05aac39..73dffe6b9 100644 --- a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js +++ b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js @@ -1,10 +1,16 @@ import coreUtils from "@opentripplanner/core-utils"; import { DirectionIcon } from "@opentripplanner/icons"; import React from "react"; +import PropTypes from "prop-types"; import * as Styled from "../styled"; +import MapillaryButton from "./mapillary-button"; -export default function AccessLegSteps({ steps }) { +export default function AccessLegSteps({ + steps, + mapillaryCallback, + mapillaryKey +}) { return ( {steps.map((step, k) => { @@ -21,7 +27,12 @@ export default function AccessLegSteps({ steps }) { {coreUtils.itinerary.getStepStreetName(step)} - + {" "} + ); @@ -31,5 +42,12 @@ export default function AccessLegSteps({ steps }) { } AccessLegSteps.propTypes = { - steps: coreUtils.types.stepsType.isRequired + steps: coreUtils.types.stepsType.isRequired, + mapillaryCallback: PropTypes.func, + mapillaryKey: PropTypes.string +}; + +AccessLegSteps.defaultProps = { + mapillaryCallback: null, + mapillaryKey: null }; diff --git a/packages/itinerary-body/src/AccessLegBody/index.js b/packages/itinerary-body/src/AccessLegBody/index.js index bd8f9201c..0b001aa89 100644 --- a/packages/itinerary-body/src/AccessLegBody/index.js +++ b/packages/itinerary-body/src/AccessLegBody/index.js @@ -6,6 +6,7 @@ import { VelocityTransitionGroup } from "velocity-react"; import AccessLegSteps from "./access-leg-steps"; import AccessLegSummary from "./access-leg-summary"; import LegDiagramPreview from "./leg-diagram-preview"; +import MapillaryButton from "./mapillary-button"; import RentedVehicleSubheader from "./rented-vehicle-subheader"; import * as Styled from "../styled"; import TNCLeg from "./tnc-leg"; @@ -38,6 +39,8 @@ class AccessLegBody extends Component { followsTransit, leg, LegIcon, + mapillaryCallback, + mapillaryKey, setLegDiagram, showElevationProfile, showLegIcon, @@ -86,7 +89,12 @@ class AccessLegBody extends Component { )} - + - {expanded && } + {expanded && ( + + )} @@ -116,6 +130,8 @@ AccessLegBody.propTypes = { leg: coreUtils.types.legType.isRequired, LegIcon: PropTypes.elementType.isRequired, legIndex: PropTypes.number.isRequired, + mapillaryCallback: PropTypes.func, + mapillaryKey: PropTypes.string, setActiveLeg: PropTypes.func.isRequired, setLegDiagram: PropTypes.func.isRequired, showElevationProfile: PropTypes.bool.isRequired, @@ -126,6 +142,8 @@ AccessLegBody.propTypes = { AccessLegBody.defaultProps = { diagramVisible: null, followsTransit: false, + mapillaryCallback: null, + mapillaryKey: null, timeOptions: null }; diff --git a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx new file mode 100644 index 000000000..315488ae2 --- /dev/null +++ b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx @@ -0,0 +1,93 @@ +import React, { useEffect, useState } from "react"; +import styled from "styled-components"; +import { StreetView } from "@styled-icons/fa-solid"; + +/** + * Helper method to generate bounding box from a location. Adding the GRAIN to the coordinate + * creates a bounding box of approximately 1 meter around the coordinate, which is likely to + * encompass any imagery available. + * @param coord The coordinate to convert to a bounding box + * @returns A bounding box 1 meter around the passed coordinate + */ +const generateBoundingBoxFromCoordinate = coord => { + const GRAIN = 0.000075; + const south = coord[0] - GRAIN; + const north = coord[0] + GRAIN; + const west = coord[1] - GRAIN; + const east = coord[1] + GRAIN; + return [west, south, east, north]; +}; + +const Container = styled.a<{ padTop?: boolean }>` + display: inline-block; + margin-top: ${props => (props.padTop ? "10px" : "0")}; + + &:hover { + cursor: pointer; + } + + &:active { + color: #111; + } +`; + +const Icon = styled(StreetView)` + height: 16px; + padding-left: 2px; +`; + +/** + * A component which shows a "street view" button if a Mapillary image is available for a + * passed coordinate + * + * @param coords The coordinates to find imagery for + * @param mapillaryKey A Mapillary api key used to check for imagery. + * @param padTop Whether to add padding to the top of the container. + * @param clickCallback A method to fire when the button is clicked, which accepts an ID. + * If it is not passsed, a popup window will be opened. */ +const MapillaryButton = ({ + clickCallback, + coords, + mapillaryKey, + padTop +}: { + clickCallback: (id: string) => void; + coords: [number, number]; + mapillaryKey: string; + padTop?: boolean; +}): JSX.Element => { + const [imageId, setImageId] = useState(null); + useEffect(() => { + const getMapillaryId = async () => { + const bounds = generateBoundingBoxFromCoordinate(coords).join(","); + const raw = await fetch( + `https://graph.mapillary.com/images?fields=id,geometry&limit=1&access_token=${mapillaryKey}&bbox=${bounds}` + ); + const json = await raw.json(); + if (json.data.length > 0) { + setImageId(json.data[0].id); + } + }; + if (!imageId && !!mapillaryKey) getMapillaryId(); + }, [coords]); + + const handleClick = () => { + if (clickCallback) clickCallback(imageId); + else { + window.open( + `https://www.mapillary.com/embed?image_key=${imageId}`, + "_blank", + "location=no,height=600,width=600,scrollbars=no,status=no" + ); + } + }; + + if (!imageId) return null; + return ( + + | + + ); +}; + +export default MapillaryButton; diff --git a/packages/itinerary-body/src/ItineraryBody/index.js b/packages/itinerary-body/src/ItineraryBody/index.js index c7619f165..145aea826 100755 --- a/packages/itinerary-body/src/ItineraryBody/index.js +++ b/packages/itinerary-body/src/ItineraryBody/index.js @@ -17,6 +17,8 @@ const ItineraryBody = ({ itinerary, LegIcon, LineColumnContent, + mapillaryCallback, + mapillaryKey, PlaceName, RouteDescription, routingType, @@ -68,6 +70,8 @@ const ItineraryBody = ({ LegIcon={LegIcon} legIndex={i} LineColumnContent={LineColumnContent} + mapillaryCallback={mapillaryCallback} + mapillaryKey={mapillaryKey} PlaceName={PlaceName} RouteDescription={RouteDescription} routingType={routingType} @@ -138,6 +142,13 @@ ItineraryBody.propTypes = { * - toRouteAbbreviation - a function to help abbreviate route names */ LineColumnContent: PropTypes.elementType.isRequired, + /** Handler for when a Mapillary button is clicked. */ + mapillaryCallback: PropTypes.func, + /** + * Mapillary key used to fetch imagery if available. Key can be obtained from + * https://www.mapillary.com/dashboard/developers + */ + mapillaryKey: PropTypes.string, /** * A custom component for rendering the place name of legs. * The component is sent 3 props: @@ -236,6 +247,8 @@ ItineraryBody.defaultProps = { className: null, diagramVisible: null, frameLeg: noop, + mapillaryCallback: null, + mapillaryKey: null, routingType: "ITINERARY", showAgencyInfo: false, showElevationProfile: false, diff --git a/packages/itinerary-body/src/ItineraryBody/place-row.js b/packages/itinerary-body/src/ItineraryBody/place-row.js index 14519e956..806a00033 100755 --- a/packages/itinerary-body/src/ItineraryBody/place-row.js +++ b/packages/itinerary-body/src/ItineraryBody/place-row.js @@ -25,6 +25,8 @@ const PlaceRow = ({ LegIcon, legIndex, LineColumnContent, + mapillaryCallback, + mapillaryKey, messages, PlaceName, RouteDescription, @@ -130,6 +132,8 @@ const PlaceRow = ({ leg={leg} LegIcon={LegIcon} legIndex={legIndex} + mapillaryCallback={mapillaryCallback} + mapillaryKey={mapillaryKey} setActiveLeg={setActiveLeg} setLegDiagram={setLegDiagram} showElevationProfile={showElevationProfile} @@ -184,6 +188,8 @@ PlaceRow.propTypes = { /** The index value of this specific leg within the itinerary */ legIndex: PropTypes.number.isRequired, LineColumnContent: PropTypes.elementType.isRequired, + mapillaryCallback: PropTypes.func, + mapillaryKey: PropTypes.string, messages: messagesType, PlaceName: PropTypes.elementType.isRequired, RouteDescription: PropTypes.elementType.isRequired, @@ -211,6 +217,8 @@ PlaceRow.defaultProps = { followsTransit: false, // can be null if this is the origin place lastLeg: null, + mapillaryCallback: null, + mapillaryKey: null, messages: { mapIconTitle: "View on map" }, diff --git a/packages/itinerary-body/src/__mocks__/handlers.js b/packages/itinerary-body/src/__mocks__/handlers.js new file mode 100644 index 000000000..4dad4c15c --- /dev/null +++ b/packages/itinerary-body/src/__mocks__/handlers.js @@ -0,0 +1,10 @@ +import { rest } from "msw"; + +import mapillary from "./mapillary.json"; + +// This faked endpoint will always return the same ID +export default [ + rest.get("https://graph.mapillary.com/images", (req, res, ctx) => { + return res(ctx.json(mapillary)); + }) +]; diff --git a/packages/itinerary-body/src/__mocks__/mapillary.json b/packages/itinerary-body/src/__mocks__/mapillary.json new file mode 100644 index 000000000..371e95f0a --- /dev/null +++ b/packages/itinerary-body/src/__mocks__/mapillary.json @@ -0,0 +1,11 @@ +{ + "data": [ + { + "id": "769386473761940", + "geometry": { + "type": "Point", + "coordinates": [-7.2089327683333, 62.245706682778] + } + } + ] +} diff --git a/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.js b/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.js index e6aac2ed1..7456d0c21 100644 --- a/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.js +++ b/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.js @@ -64,6 +64,7 @@ export default class ItineraryBodyDefaultsWrapper extends Component { itinerary={itinerary} LegIcon={LegIcon} LineColumnContent={LineColumnContent || DefaultLineColumnContent} + mapillaryKey="fake key, but ok because the api response is also fake" PlaceName={PlaceName || DefaultPlaceName} RouteDescription={RouteDescription || DefaultRouteDescription} routingType="ITINERARY" From 1bf4a7deef1d92ad112b9bf2e708583a62fc593a Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 12 Jan 2022 13:54:39 +0100 Subject: [PATCH 2/8] refactor(itinerary-body/mapillary-button): clean up mapillary additions --- .../itinerary-body/src/AccessLegBody/mapillary-button.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx index 315488ae2..827220aa5 100644 --- a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx +++ b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx @@ -51,23 +51,26 @@ const MapillaryButton = ({ mapillaryKey, padTop }: { - clickCallback: (id: string) => void; + clickCallback?: (id: string) => void; coords: [number, number]; mapillaryKey: string; padTop?: boolean; }): JSX.Element => { const [imageId, setImageId] = useState(null); + useEffect(() => { + // useEffect only supports async actions as a child function const getMapillaryId = async () => { const bounds = generateBoundingBoxFromCoordinate(coords).join(","); const raw = await fetch( - `https://graph.mapillary.com/images?fields=id,geometry&limit=1&access_token=${mapillaryKey}&bbox=${bounds}` + `https://graph.mapillary.com/images?fields=id&limit=1&access_token=${mapillaryKey}&bbox=${bounds}` ); const json = await raw.json(); if (json.data.length > 0) { setImageId(json.data[0].id); } }; + if (!imageId && !!mapillaryKey) getMapillaryId(); }, [coords]); From a53ec202d1a2abc7d434be601a7a7c7e9a514264 Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 12 Jan 2022 16:16:28 +0100 Subject: [PATCH 3/8] refactor(itinerary-body/AccessLegBody): correctly name prop --- packages/itinerary-body/src/AccessLegBody/access-leg-steps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js index 73dffe6b9..8b418f02c 100644 --- a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js +++ b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js @@ -30,7 +30,7 @@ export default function AccessLegSteps({ {" "} From 89d3e1c30e9d2bede62ba46fe7cd3e97430006ba Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Thu, 13 Jan 2022 13:49:34 +0100 Subject: [PATCH 4/8] refactor(itinerary-body/AccessLegBody): clean up, prefer css over html for padding --- .../src/AccessLegBody/access-leg-steps.js | 3 +- .../src/AccessLegBody/mapillary-button.tsx | 29 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js index 8b418f02c..ae9bb2f43 100644 --- a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js +++ b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js @@ -27,11 +27,12 @@ export default function AccessLegSteps({ {coreUtils.itinerary.getStepStreetName(step)} - {" "} + diff --git a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx index 827220aa5..3d7dfcc90 100644 --- a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx +++ b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx @@ -3,32 +3,39 @@ import styled from "styled-components"; import { StreetView } from "@styled-icons/fa-solid"; /** - * Helper method to generate bounding box from a location. Adding the GRAIN to the coordinate + * Helper method to generate bounding box from a location. Adding the WINDOW to the coordinate * creates a bounding box of approximately 1 meter around the coordinate, which is likely to * encompass any imagery available. * @param coord The coordinate to convert to a bounding box * @returns A bounding box 1 meter around the passed coordinate */ -const generateBoundingBoxFromCoordinate = coord => { - const GRAIN = 0.000075; - const south = coord[0] - GRAIN; - const north = coord[0] + GRAIN; - const west = coord[1] - GRAIN; - const east = coord[1] + GRAIN; +const generateBoundingBoxFromCoordinate = (coord: [number, number]) => { + const WINDOW = 0.000075; + const south = coord[0] - WINDOW; + const north = coord[0] + WINDOW; + const west = coord[1] - WINDOW; + const east = coord[1] + WINDOW; return [west, south, east, north]; }; -const Container = styled.a<{ padTop?: boolean }>` +const Container = styled.a<{ padLeft?: boolean; padTop?: boolean }>` display: inline-block; margin-top: ${props => (props.padTop ? "10px" : "0")}; &:hover { cursor: pointer; + text-decoration: none; } &:active { color: #111; } + + &::before { + content: "| "; + margin-left: ${props => (props.padLeft ? "1ch" : "0")}; + cursor: auto; + } `; const Icon = styled(StreetView)` @@ -49,11 +56,13 @@ const MapillaryButton = ({ clickCallback, coords, mapillaryKey, + padLeft, padTop }: { clickCallback?: (id: string) => void; coords: [number, number]; mapillaryKey: string; + padLeft?: boolean; padTop?: boolean; }): JSX.Element => { const [imageId, setImageId] = useState(null); @@ -87,8 +96,8 @@ const MapillaryButton = ({ if (!imageId) return null; return ( - - | + + ); }; From 546c0eaecd44cf9a0fc127fe7c8a8f2b0f4b966d Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Thu, 13 Jan 2022 13:50:29 +0100 Subject: [PATCH 5/8] refactor: clarify Mapillary lonlat format --- packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx index 3d7dfcc90..4dbce9b79 100644 --- a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx +++ b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx @@ -47,7 +47,7 @@ const Icon = styled(StreetView)` * A component which shows a "street view" button if a Mapillary image is available for a * passed coordinate * - * @param coords The coordinates to find imagery for + * @param coords The coordinates to find imagery for in the format [lat, lon] * @param mapillaryKey A Mapillary api key used to check for imagery. * @param padTop Whether to add padding to the top of the container. * @param clickCallback A method to fire when the button is clicked, which accepts an ID. From 51ff135b924146510a8586508d22fd26ff56d2ae Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Thu, 13 Jan 2022 14:16:27 +0100 Subject: [PATCH 6/8] refactor: sort props --- .../itinerary-body/src/AccessLegBody/access-leg-steps.js | 2 +- .../src/AccessLegBody/mapillary-button.tsx | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js index ae9bb2f43..7e48a31a5 100644 --- a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js +++ b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js @@ -29,8 +29,8 @@ export default function AccessLegSteps({ {coreUtils.itinerary.getStepStreetName(step)} diff --git a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx index 4dbce9b79..c1f06390b 100644 --- a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx +++ b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx @@ -33,8 +33,8 @@ const Container = styled.a<{ padLeft?: boolean; padTop?: boolean }>` &::before { content: "| "; - margin-left: ${props => (props.padLeft ? "1ch" : "0")}; cursor: auto; + margin-left: ${props => (props.padLeft ? "1ch" : "0")}; } `; @@ -96,7 +96,12 @@ const MapillaryButton = ({ if (!imageId) return null; return ( - + ); From d4d9d062406cf5631a718bd2fde9a7ad2c8d6427 Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Thu, 13 Jan 2022 14:48:09 +0100 Subject: [PATCH 7/8] refactor(itinerary-body/AccessLegBody): don't crash on server fail --- packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx index c1f06390b..12985ae00 100644 --- a/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx +++ b/packages/itinerary-body/src/AccessLegBody/mapillary-button.tsx @@ -75,7 +75,7 @@ const MapillaryButton = ({ `https://graph.mapillary.com/images?fields=id&limit=1&access_token=${mapillaryKey}&bbox=${bounds}` ); const json = await raw.json(); - if (json.data.length > 0) { + if (json?.data?.length > 0) { setImageId(json.data[0].id); } }; From db984ce117a40465d9a64def798af631a0dd0679 Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 19 Jan 2022 08:34:27 -0500 Subject: [PATCH 8/8] refactor(itinerary-body/AccessLegBody): address pr feedback --- .../src/AccessLegBody/access-leg-steps.js | 2 +- .../itinerary-body/src/AccessLegBody/index.js | 2 +- .../src/AccessLegBody/mapillary-button.tsx | 20 ++++++++++++------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js index 7e48a31a5..1bd217f7b 100644 --- a/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js +++ b/packages/itinerary-body/src/AccessLegBody/access-leg-steps.js @@ -30,7 +30,7 @@ export default function AccessLegSteps({ diff --git a/packages/itinerary-body/src/AccessLegBody/index.js b/packages/itinerary-body/src/AccessLegBody/index.js index 0b001aa89..27fe522e2 100644 --- a/packages/itinerary-body/src/AccessLegBody/index.js +++ b/packages/itinerary-body/src/AccessLegBody/index.js @@ -90,7 +90,7 @@ class AccessLegBody extends Component { )} { +const generateBoundingBoxFromCoordinate = ({ + lat, + lon +}: { + lat: number; + lon: number; +}) => { const WINDOW = 0.000075; - const south = coord[0] - WINDOW; - const north = coord[0] + WINDOW; - const west = coord[1] - WINDOW; - const east = coord[1] + WINDOW; + const south = lat - WINDOW; + const north = lat + WINDOW; + const west = lon - WINDOW; + const east = lon + WINDOW; return [west, south, east, north]; }; @@ -60,7 +66,7 @@ const MapillaryButton = ({ padTop }: { clickCallback?: (id: string) => void; - coords: [number, number]; + coords: { lat: number; lon: number }; mapillaryKey: string; padLeft?: boolean; padTop?: boolean; @@ -102,7 +108,7 @@ const MapillaryButton = ({ padTop={padTop} title="Show street imagery at this location" > - + ); };