diff --git a/web/client/plugins/StreetView/StreetView.jsx b/web/client/plugins/StreetView/StreetView.jsx index de56a078a9..82c42646a3 100644 --- a/web/client/plugins/StreetView/StreetView.jsx +++ b/web/client/plugins/StreetView/StreetView.jsx @@ -59,7 +59,8 @@ const StreetViewPluginContainer = connect(() => ({}), { * - `cyclomedia` provider: The API key is mandatory and can be configured only in the plugin configuration. It is not possible to configure it globally in `localConfig.json`, in `apiKeys.cyclomediaAPIKey`. * @property {string} providerSettings The settings specific for the provider. Depending on the `provider` property, the following settings are available: * - `cyclomedia` provider: - * - `StreetSmartApiURL` (optional). The URL of the StreetSmart API. Default: `https://streetsmart.cyclomedia.com/api/v23.7/StreetSmartApi.js`. + * - `providerSettings.StreetSmartApiURL` (optional). The URL of the StreetSmart API. Default: `https://streetsmart.cyclomedia.com/api/v23.7/StreetSmartApi.js`. + * - `providerSettings.srs` (optional). Coordinate reference system code to use for the API. Default: `EPSG:4326`. Note that the SRS used here must be supported by the StreetSmart API **and** defined in `localConfig.json` file, in `projectionDefs`. * * Generally speaking, you should prefer general settings in `localConfig.json` over the plugin configuration, in order to reuse the same configuration for default viewer and all the contexts, automatically. This way you will not need to configure the `apiKey` in every context. *
**Important**: You can use only **one** API-key for a MapStore instance. The api-key can be configured replicated in every plugin configuration or using one of the unique global settings (suggested) in `localConfig.json`). @see {@link https://github.com/googlemaps/js-api-loader/issues/5|here} and @see {@link https://github.com/googlemaps/js-api-loader/issues/100|here} diff --git a/web/client/plugins/StreetView/components/CyclomediaView/CyclomediaView.js b/web/client/plugins/StreetView/components/CyclomediaView/CyclomediaView.js index 038141dbfe..5c7e23d27a 100644 --- a/web/client/plugins/StreetView/components/CyclomediaView/CyclomediaView.js +++ b/web/client/plugins/StreetView/components/CyclomediaView/CyclomediaView.js @@ -1,5 +1,8 @@ import React, {useState, useEffect, useRef} from 'react'; import Message from '../../../../components/I18N/Message'; +import { isProjectionAvailable } from '../../../../utils/ProjectionUtils'; +import { reproject } from '../../../../utils/CoordinatesUtils'; + import { getCredentials as getStoredCredentials, setCredentials as setStoredCredentials } from '../../../../utils/SecurityUtils'; import { CYCLOMEDIA_CREDENTIALS_REFERENCE } from '../../constants'; @@ -7,7 +10,7 @@ import { Alert, Button } from 'react-bootstrap'; import CyclomediaCredentials from './Credentials'; import EmptyStreetView from '../EmptyStreetView'; - +const PROJECTION_NOT_AVAILABLE = "Projection not available"; const isInvalidCredentials = (error) => { return error?.message?.indexOf?.("code 401"); }; @@ -17,11 +20,13 @@ const isInvalidCredentials = (error) => { * @param {object|string} error the error to parse * @returns {string|JSX.Element} the error message */ -const getErrorMessage = (error) => { +const getErrorMessage = (error, msgParams = {}) => { if (isInvalidCredentials(error) >= 0) { - return ; + return ; + } + if (error?.message?.indexOf?.(PROJECTION_NOT_AVAILABLE) >= 0) { + return ; } - return error?.message ?? "Unknown error"; }; @@ -88,6 +93,7 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo `; const initOptions = providerSettings?.initOptions ?? {}; + const srs = providerSettings?.srs ?? 'EPSG:4326'; // for measurement tool and oblique tool 'EPSG:7791' is one of the supported SRS // location contains the latLng and the properties of the feature // properties contains the `imageId` that can be used as query const {properties} = location; @@ -114,12 +120,16 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo setStoredCredentials(CYCLOMEDIA_CREDENTIALS_REFERENCE, undefined); } }; + // setting a custom srs enables the measurement tool (where present) and other tools, but coordinates of the click + // will be managed in the SRS used, so we need to convert them to EPSG:4326. + // So we need to make sure that the SRS is available for coordinates conversion + useEffect(() => { + if (!isProjectionAvailable(srs)) { + console.error(`Cyclomedia API: init: error: projection ${srs} is not available`); + setError(new Error(PROJECTION_NOT_AVAILABLE)); + } + }, [srs]); - const srs = 'EPSG:4326'; - // this EPSG:7791 enables the measurement tool (where present), but coordinates of the click - // are in the srs named, so we need to convert them to EPSG:4326. Actually definition is not in place - // and have to be implemented - // it enables also the oblique tool, but we have to implement the click on that point too. /** * Utility function to open an image in street smart viewer (it must be called after the API is initialized) @@ -142,6 +152,11 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo measureTypeButtonVisible: true, measureTypeButtonStart: true, measureTypeButtonToggle: true + }, + obliqueViewer: { + closable: true, + maximizable: true, + navbarVisible: false } }; return StreetSmartApi.open(query, options); @@ -149,7 +164,7 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo // initialize API useEffect(() => { - if (!StreetSmartApi || !username || !password || !apiKey) return () => {}; + if (!StreetSmartApi || !username || !password || !apiKey || !isProjectionAvailable(srs)) return () => {}; setInitializing(true); StreetSmartApi.init({ targetElement, @@ -198,10 +213,11 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo const {recording} = detail ?? {}; // extract coordinates lat long from `xyz` of `recording` and `imageId` from recording `id` property if (recording?.xyz && recording?.id) { + const {x: lng, y: lat} = reproject([recording?.xyz?.[0], recording?.xyz?.[1]], srs, 'EPSG:4326'); setLocation({ latLng: { - lat: recording?.xyz?.[1], - lng: recording?.xyz?.[0] + lat, + lng }, properties: { ...recording, @@ -286,7 +302,7 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo - {getErrorMessage(error)} + {getErrorMessage(error, {srs})} {initialized ?