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

#9894 Add possibility to configure srs in StreetView #9897

Merged
merged 1 commit into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion web/client/plugins/StreetView/StreetView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <br>**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}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
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';
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");
};
Expand All @@ -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 <Message msgId="streetView.cyclomedia.errors.invalidCredentials" />;
return <Message msgId="streetView.cyclomedia.errors.invalidCredentials" msgParams={msgParams} />;
}
if (error?.message?.indexOf?.(PROJECTION_NOT_AVAILABLE) >= 0) {
return <Message msgId="streetView.cyclomedia.errors.projectionNotAvailable" msgParams={msgParams} />;
}

return error?.message ?? "Unknown error";
};

Expand Down Expand Up @@ -88,6 +93,7 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
`;
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;
Expand All @@ -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)
Expand All @@ -142,14 +152,19 @@ 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);
};

// initialize API
useEffect(() => {
if (!StreetSmartApi || !username || !password || !apiKey) return () => {};
if (!StreetSmartApi || !username || !password || !apiKey || !isProjectionAvailable(srs)) return () => {};
setInitializing(true);
StreetSmartApi.init({
targetElement,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -286,7 +302,7 @@ const CyclomediaView = ({ apiKey, style, location = {}, setPov = () => {}, setLo
</iframe>
<Alert bsStyle="danger" style={{...style, textAlign: 'center', alignContent: 'center', display: showError ? 'block' : 'none'}} key="error">
<Message msgId="streetView.cyclomedia.errorOccurred" />
{getErrorMessage(error)}
{getErrorMessage(error, {srs})}
{initialized ? <div><Button
onClick={() => {
setError(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ describe('Cyclomedia CyclomediaView', () => {
setCredentials(CYCLOMEDIA_CREDENTIALS_REFERENCE, {username: 'test', password: 'password'});
// set a default mock
window.__cyclomedia__mock__ = {
init: () => {},
open: () => {}
init: () => new Promise((resolve) => { resolve(); }),
open: () => new Promise((resolve) => { resolve(); })
};
setTimeout(done);
});
Expand Down Expand Up @@ -102,5 +102,24 @@ describe('Cyclomedia CyclomediaView', () => {
expect(div).toExist();

});

it('srs check for existing projection', () => {
const props = {
apiKey: testAPIKey
};
ReactDOM.render(<CyclomediaView {...props} providerSettings={{...mockProviderSettings, srs: "EPSG:4326"}}/>, document.getElementById("container"));
// no error shown.
const div = emptyStreetView();
expect(div).toExist();
});
it('srs not defined error handling', () => {
const props = {
apiKey: testAPIKey
};
ReactDOM.render(<CyclomediaView {...props} providerSettings={{...mockProviderSettings, srs: "UNKNOWN"}}/>, document.getElementById("container"));
// no error shown.
const div = emptyStreetView();
expect(div).toNotExist();
// check alert to exist
expect(document.querySelector('.alert-danger')).toExist();
});
});
3 changes: 2 additions & 1 deletion web/client/translations/data.de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -3941,7 +3941,8 @@
"zoomIn": "Bitte zoomen Sie in die Karte, um die Street-View-Abdeckung zu sehen",
"errorOccurred": "Fehler aufgetreten: ",
"errors": {
"invalidCredentials": "Ungültige Anmeldeinformationen"
"invalidCredentials": "Ungültige Anmeldeinformationen",
"projectionNotAvailable": "Die konfigurierte Projektion {srs} ist nicht verfügbar. Bitte kontaktieren Sie den Administrator."
},
"reloadAPI": "API neu laden"
}
Expand Down
3 changes: 2 additions & 1 deletion web/client/translations/data.en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3914,7 +3914,8 @@
"zoomIn": "Please zoom in the map to see the street view coverage",
"errorOccurred": "Error occurred: ",
"errors": {
"invalidCredentials": "Invalid credentials"
"invalidCredentials": "Invalid credentials",
"projectionNotAvailable": "The projection {srs} configured is not available. Please contact the administrator."
},
"reloadAPI": "Reload API"
}
Expand Down
3 changes: 2 additions & 1 deletion web/client/translations/data.es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -3903,7 +3903,8 @@
"zoomIn": "Por favor, acerque el mapa para ver la cobertura de vista de calle",
"errorOccurred": "Se produjo un error: ",
"errors": {
"invalidCredentials": "Credenciales inválidas"
"invalidCredentials": "Credenciales inválidas",
"projectionNotAvailable": "La proyección {srs} configurada no está disponible. Por favor, contacte al administrador."
},
"reloadAPI": "Recargar API"
}
Expand Down
3 changes: 2 additions & 1 deletion web/client/translations/data.fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -3903,7 +3903,8 @@
"zoomIn": "Veuillez zoomer sur la carte pour voir la couverture Street View",
"errorOccurred": "Une erreur s'est produite : ",
"errors": {
"invalidCredentials": "Identifiants invalides"
"invalidCredentials": "Identifiants invalides",
"projectionNotAvailable": "La projection {srs} configurée n'est pas disponible. Veuillez contacter l'administrateur."
},
"reloadAPI": "Recharger l'API"
}
Expand Down
3 changes: 2 additions & 1 deletion web/client/translations/data.it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -3903,7 +3903,8 @@
"zoomIn": "Effettua un ulteriore zoom sulla mappa per visualizzare dove sono disponibili le immagini",
"errorOccurred": "Si è verificato un errore: ",
"errors": {
"invalidCredentials": "Credenziali non valide"
"invalidCredentials": "Credenziali non valide",
"projectionNotAvailable": "La proiezione {srs} configurata non è disponibile. Contattare l'amministratore."
},
"reloadAPI": "Ricarica API"
}
Expand Down
Loading