Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge indexing prevention #181

Merged
merged 9 commits into from
Dec 20, 2024
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="robots" content="noindex,nofollow" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
Expand Down
13 changes: 8 additions & 5 deletions src/components/MapillaryViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getClosestMapillaryImage } from "../utils/mapUtils";
import styles from "./mapillaryViewer.module.css";

const MapillaryViewer = observer(
({ location, elementId, onNavigation, className, onCloseViewer }) => {
({ location, elementId, onNavigation, className, onCloseViewer, selectedRoutes }) => {
const [error, setError] = useState(null);
const mly = useRef(null);
const resizeListener = useRef(null);
Expand Down Expand Up @@ -44,9 +44,10 @@ const MapillaryViewer = observer(

window.addEventListener("resize", currentResizeListener);
resizeListener.current = currentResizeListener;

currentMly.setFilter(["==", "organizationKey", "227572519135262"]);
currentMly.on("image", (evt) => onNavigation(evt.image.lngLat));
currentMly.on("image", (evt) => {
onNavigation({latlng: evt.image.lngLat, computedCompassAngle: evt.image.computedCompassAngle})
});
mly.current = currentMly;
}, [mly.current, resizeListener.current]);

Expand All @@ -57,19 +58,21 @@ const MapillaryViewer = observer(
const closest = await getClosestMapillaryImage({
lat: location.lat,
lng: location.lng,
selectedRoutes
});
if (closest && closest.id) {
mly.current
.moveTo(closest.id)
.then((node) => {
onNavigation(node.lngLat);
.then((image) => {
onNavigation({latlng: image.lngLat, computedCompassAngle: image.computedCompassAngle});
})
.catch((error) => console.warn(error));
setError(null);
} else {
setError("Katukuvia ei löytynyt.");
}
} catch (e) {
console.log(e)
setError("Katunäkymän haku epäonnistui.");
}
}
Expand Down
1 change: 0 additions & 1 deletion src/components/mapContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ const restroomQuery = gql`
type
lat
lon
point
dateImported
mode
}
Expand Down
88 changes: 43 additions & 45 deletions src/components/mapLeaflet.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import locationIconOffline from "../icons/icon-location-offline.svg";
import fullScreenEnterIcon from "../icons/icon-fullscreen-enter.svg";
import fullScreenExitIcon from "../icons/icon-fullscreen-exit.svg";
import restroomIcon from "../icons/restroom-solid.svg";
import { mapillaryImageMargerIcon } from '../icons/mapillaryImageMargerIcon.js';
import styles from "./mapLeaflet.module.css";
import MapillaryViewer from "./MapillaryViewer.js";
import { isMobile } from "../utils/browser";
Expand Down Expand Up @@ -83,11 +84,11 @@ const addMarkersToLayer = (stops, direction, map, restrooms) => {
}
closeByRestrooms.forEach((closeByRestroom) => {
let icon = mapIcon(restroomIcon);
const markerr = L.marker([closeByRestroom.lat, closeByRestroom.lon], { icon });
markerr.bindTooltip(`${closeByRestroom.nameFi}, ${closeByRestroom.addressFi}`, {
const marker = L.marker([closeByRestroom.lat, closeByRestroom.lon], { icon });
marker.bindTooltip(`${closeByRestroom.nameFi}, ${closeByRestroom.addressFi}`, {
direction: "top",
});
markerr.addTo(map);
marker.addTo(map);
});
};

Expand Down Expand Up @@ -296,18 +297,6 @@ class MapLeaflet extends React.Component {
}

componentDidUpdate(prevProps, prevState) {
// All layers except the base layer and mapillary features are removed when the component is updated
this.map.eachLayer((layer) => {
if (!layer.options.baseLayer) {
if (
layer.options.type !== "mapillaryGeoJsonLayer" &&
layer.options.type !== "mapillaryImageMarker" &&
layer.options.type !== "mapillaryHighlightMarker"
) {
this.map.removeLayer(layer);
}
}
});
// Leaflet map is updated once geometry and stop data has been fetched
// The view (bounding box) is set only the first time the route stops are recieved
if (
Expand Down Expand Up @@ -468,6 +457,12 @@ class MapLeaflet extends React.Component {
drawControl: true,
});

if (this.map) {
this.map.createPane('imageMarkerPane');
this.map.getPane('imageMarkerPane').style.zIndex = 650;
this.map.getPane('imageMarkerPane').style.pointerEvents = 'none';
}

const baseMaps = {
Aerial: aerialTileLayer,
Digitransit: digitransitTileLayer,
Expand Down Expand Up @@ -524,44 +519,43 @@ class MapLeaflet extends React.Component {
if (!this.state.showMapillaryLayer) {
return;
}
for (const key in e.target._layers) {
const layer = e.target._layers[key];
if (layer) {
if (layer.options.type === "mapillaryHighlightMarker") {
this.setState({ mapillaryLocation: layer._latlng });
}
e.target.eachLayer(layer => {
if (layer.options && layer.options.type === "mapillaryHighlightMarker") {
const latLng = layer.getLatLng();
this.setState({ mapillaryLocation: latLng });
}
}
});
}

setMapillaryLocation(position, playService) {
if (!this.state.mapillaryLocation) {
if (playService) {
playService.stop();
}
return;


setMapillaryLocation({latlng, computedCompassAngle = 0}) {
function createRotatedIcon(computedCompassAngle) {
const svgIconHtml = mapillaryImageMargerIcon(computedCompassAngle);

const svgIcon = L.divIcon({
className: 'custom-div-icon',
html: svgIconHtml,
iconSize: [15, 15],
iconAnchor: [7.5, 7.5]
});

return svgIcon;
}
if (this.imageMarker) {
this.imageMarker.remove();
this.imageMarker = null;
}
if (this.map && position) {
const marker = this.createMarker({
position: position,
opacity: 1,
type: "mapillaryImageMarker",
color: "red",
});
if (!this.imageMarker) {
this.imageMarker = marker;
this.imageMarker.addTo(this.map);
} else {
this.imageMarker.setLatLng(position);
}
} else if (!position) {

if (this.map && latlng) {
const marker = L.marker(latlng, {
icon: createRotatedIcon(computedCompassAngle)
}).addTo(this.map);

this.imageMarker = marker;
} else if (!latlng) {
this.removeMarker();
}
this.setState({ mapillaryImageLocation: { lat: position.lat, lng: position.lon } });
this.setState({ mapillaryImageLocation: { lat: latlng.lat, lng: latlng.lng } });
}

onHover = (e) => {
Expand Down Expand Up @@ -597,6 +591,7 @@ class MapLeaflet extends React.Component {
radius: 4,
color: options.color,
opacity: options.opacity,
pane: 'imageMarkerPane',
});
};

Expand Down Expand Up @@ -628,8 +623,10 @@ class MapLeaflet extends React.Component {
});
this.setState({ mapillaryLocation: null });
};

render() {
const selectedRouteObjects = this.props.routes.filter(route =>
this.props.selectedRoutes.includes(route.id)
);
return (
<div
className={classNames(styles.container, {
Expand All @@ -655,6 +652,7 @@ class MapLeaflet extends React.Component {
elementId="mly"
onNavigation={this.setMapillaryLocation}
location={this.state.mapillaryLocation}
selectedRoutes={selectedRouteObjects}
/>
)}
</div>
Expand Down
11 changes: 1 addition & 10 deletions src/components/stop.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@ import styles from "./stop.module.css";
import PropTypes from "prop-types";
import timeIcon2 from "../icons/icon-time2.svg";

const Stop = ({
shortId,
stopNameFi,
stopNameSv,
duration,
platform,
onClick,
timingStopType,
}) => (
const Stop = ({ shortId, stopNameFi, stopNameSv, platform, onClick, timingStopType }) => (
<button className={styles.root} onClick={onClick}>
<p className={styles.textTitle}>{shortId}</p>
<div className={styles.infoContainer}>
Expand All @@ -21,7 +13,6 @@ const Stop = ({
</div>
<div className={styles.additionalInfoContainer}>
<div className={styles.textAdditional}>{stopNameSv}</div>
<div className={styles.textDuration}>{duration} min</div>
</div>
</div>
{timingStopType > 0 && (
Expand Down
2 changes: 1 addition & 1 deletion src/components/stop.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
.textTitle {
font-size: var(--mainTextSize);
font-weight: 600;
width: 68px;
width: 100px;
flex-wrap: wrap;
}

Expand Down
1 change: 0 additions & 1 deletion src/components/stopList.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const renderStops = (routeStops, isFullScreen, setMapCenter) =>
shortId={stop.shortId}
stopNameFi={stop.nameFi}
stopNameSv={stop.nameSe}
duration={stop.duration}
timingStopType={stop.timingStopType}
platform={stop.platform}
onClick={() => setMapCenter({ lat: stop.lat, lng: stop.lon, stopId: stop.stopId })}
Expand Down
7 changes: 7 additions & 0 deletions src/icons/mapillaryImageMargerIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

export function mapillaryImageMargerIcon(angle) {
const correctedAngle = angle - 90;
return `<svg fill="red" width="15px" height="15px" viewBox="-96 0 512 512" xmlns="http://www.w3.org/2000/svg" style="transform: rotate(${correctedAngle}deg); transform-origin: center;">` +
`<path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/>` +
`</svg>`;
}
63 changes: 59 additions & 4 deletions src/utils/mapUtils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as turf from "@turf/turf";

const IMAGE_COMPASS_ANGLE_THRESHOLD = 20;

const wait = async (delay) => {
return new Promise((resolve) => setTimeout(resolve, delay));
};
Expand All @@ -18,12 +20,63 @@ const fetchRetry = async (url, delay, tries, fetchOptions = {}) => {
return res;
};

export async function getClosestMapillaryImage({ lat, lng }) {

const bearingToCompassAngle = (bearing) => {
const compassAngle = (bearing + 360) % 360;
return compassAngle;
};

export async function getCompassAngle({ closestCoordinate, nextCoordinate }) {
const startPoint = turf.point(closestCoordinate);
const endPoint = turf.point(nextCoordinate);

const bearing = turf.bearing(startPoint, endPoint);

const compassAngle = bearingToCompassAngle(bearing);

return compassAngle;
}

export async function getClosestMapillaryImage({ lat, lng, selectedRoutes }) {

const p = turf.point([lng, lat]);
const buffer = turf.buffer(p, 0.05, { units: "kilometers" });
const bbox = turf.bbox(buffer);

const url = `https://graph.mapillary.com/images?fields=id,geometry&bbox=${bbox}&limit=100`;
const pointsWithGeometry = [];

selectedRoutes.forEach((route) => {
route.geometries.nodes[0].geometry.coordinates.forEach((coord) => {
pointsWithGeometry.push({
point: turf.point(coord),
geometry: route.geometries.nodes[0].geometry.coordinates,
});
});
});

const pointsCollection = turf.featureCollection(pointsWithGeometry.map(pg => pg.point));

const nearest = turf.nearestPoint(p, pointsCollection);

const nearestGeometry = pointsWithGeometry.find(pg =>
pg.point.geometry.coordinates[0] === nearest.geometry.coordinates[0] &&
pg.point.geometry.coordinates[1] === nearest.geometry.coordinates[1]
).geometry;

let nextCoordinate;
const nearestCoordIndex = nearestGeometry.findIndex(coord =>
coord[0] === nearest.geometry.coordinates[0] &&
coord[1] === nearest.geometry.coordinates[1]
);

if (nearestCoordIndex >= 0 && nearestCoordIndex < nearestGeometry.length - 5) {
nextCoordinate = nearestGeometry[nearestCoordIndex + 5];
} else {
nextCoordinate = nearest.geometry.coordinates;
}

const closestCoordinateCompassAngle = await getCompassAngle({closestCoordinate: nearest.geometry.coordinates, nextCoordinate})
const url = `https://graph.mapillary.com/images?fields=id,geometry,compass_angle,detection&bbox=${bbox}&limit=100`;
const delay = 500;
const tries = 3;

Expand Down Expand Up @@ -52,18 +105,20 @@ export async function getClosestMapillaryImage({ lat, lng }) {
if (!json.data) {
return null;
}

let closest;
json.data.forEach((feature) => {
const coordinates = feature.geometry.coordinates;
let distance = Math.hypot(
Math.abs(lat - coordinates[1]),
Math.abs(lng - coordinates[0])
);
if (!closest || distance < closest.distance) {
const angleDifference = Math.abs(feature.compass_angle - closestCoordinateCompassAngle);
const angleOffBy = Math.min(angleDifference, 360 - angleDifference);
if ((!closest || distance < closest.distance) && angleOffBy <= IMAGE_COMPASS_ANGLE_THRESHOLD) {
closest = feature;
closest.distance = distance;
}
});

return closest;
}
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6439,8 +6439,8 @@ hpack.js@^2.1.6:
wbuf "^1.1.0"

"hsl-map-style@https://github.com/HSLdevcom/hsl-map-style":
version "1.1.2"
resolved "https://github.com/HSLdevcom/hsl-map-style#207a8de2664c9a50b5a5f886d30f5b1628f995c7"
version "1.2.0"
resolved "https://github.com/HSLdevcom/hsl-map-style#68b1642ce364ebcea115bc3f20465d3c2ee83c0d"
dependencies:
lodash "^4.17.4"

Expand Down
Loading