Commit 653a7317 authored by Oleksii Savran's avatar Oleksii Savran Committed by Matija Obreza
Browse files

Map: AccessionDisplay

parent 6fbe158b
...@@ -42,6 +42,13 @@ Add a container `<div>` to your HTML page and initialize the Genesys UI with the ...@@ -42,6 +42,13 @@ Add a container `<div>` to your HTML page and initialize the Genesys UI with the
enabled: true, // Enable shopping cart enabled: true, // Enable shopping cart
}, },
recaptchaSiteKey: '<public key for your site>', // Public ReCaptcha site key recaptchaSiteKey: '<public key for your site>', // Public ReCaptcha site key
map: {
enabled: true, // Enable map
baseMap: {
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}/', // tile server url for the base layer
attribution: 'Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ' // attribution data
},
}
}; };
// Show UI // Show UI
...@@ -70,6 +77,7 @@ to *@geneesys-pgr/ui-embedded*: ...@@ -70,6 +77,7 @@ to *@geneesys-pgr/ui-embedded*:
|`title`| *Genesys:* | HTML page title prefix | |`title`| *Genesys:* | HTML page title prefix |
| `accession` | `{ ... }` | See *Accession* configuration options | | `accession` | `{ ... }` | See *Accession* configuration options |
| `shoppingCart` | `{ ... }` | See *Shopping cart* configuration options | | `shoppingCart` | `{ ... }` | See *Shopping cart* configuration options |
| `map` | `{ ... }` | See *Map* configuration |
Additional configuration options are described below. Additional configuration options are described below.
...@@ -103,3 +111,18 @@ const genesysConfig = { ...@@ -103,3 +111,18 @@ const genesysConfig = {
|---|---|---| |---|---|---|
|`enabled`|`false`| Toggle Shopping cart functionality | |`enabled`|`false`| Toggle Shopping cart functionality |
### Map configuration
The map is **disabled by default**. To allow users to see map of accession collecting site on accession details page and access the map page you must explicity enable it in your configuration and provide a tile server URL and an attribution data for the base map layer:
|Property|Default|Description|
|---|---|---|
|`enabled`|`false`| Toggle map functionality |
|`baseMap`|`{...}`| See baseMap |
##### baseMap
|Property|Default|Description|
|---|---|---|
|`url`|`https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}/`| Tile server url for the base layer |
|`attribution`|`Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ`| Attribution data |
...@@ -79,13 +79,13 @@ module.exports = { ...@@ -79,13 +79,13 @@ module.exports = {
noEmitOnErrors: true, noEmitOnErrors: true,
splitChunks: { splitChunks: {
chunks: 'all', chunks: 'all',
// cacheGroups: { cacheGroups: {
// deps: { deps: {
// test: /[\\/]node_modules[\\/]/, test: /[\\/]node_modules[\\/]/,
// name: 'deps', name: 'deps',
// chunks: 'all' chunks: 'all'
// }, },
// }, },
}, },
}, },
}; };
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<div class="container"> <div class="container">
<div class="jumbotron small"> <div class="jumbotron small">
<h1 class="display-6">Embedded Genesys</h1> <h1 class="display-6">Embedded Genesys</h1>
<p class="lead">This page demonstrates embedding Genesys data for <code>COL003</code> and <code>BEL084</code> <p class="lead">This page demonstrates embedding Genesys data for <code>COL003</code> and <code>BEL084</code>
from <a href="https://www.genesys-pgr.org" target="_blank">Genesys</a> in a 3rd-party website.<br /> from <a href="https://www.genesys-pgr.org" target="_blank">Genesys</a> in a 3rd-party website.<br />
The current release of the Javascript library is available on <a The current release of the Javascript library is available on <a
href="https://www.npmjs.com/package/@genesys-pgr/ui-embedded">npmjs.com</a>.</p> href="https://www.npmjs.com/package/@genesys-pgr/ui-embedded">npmjs.com</a>.</p>
...@@ -33,7 +33,10 @@ ...@@ -33,7 +33,10 @@
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#/cart">Cart</a> <a class="nav-link" href="#/cart">Cart</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="#/map">Map</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="index.html">EN</a> <a class="nav-link" href="index.html">EN</a>
</li> </li>
......
...@@ -10,6 +10,7 @@ import { WithConfig } from 'config/config'; ...@@ -10,6 +10,7 @@ import { WithConfig } from 'config/config';
import { CountryName } from 'ui/common/CountryName'; import { CountryName } from 'ui/common/CountryName';
import GrinSpecies from 'ui/common/GrinSpecies'; import GrinSpecies from 'ui/common/GrinSpecies';
import PdciTable from 'ui/common/PdciTable'; import PdciTable from 'ui/common/PdciTable';
import LocationMap from 'map/LocationMap';
import Loading from 'ui/common/Loading'; import Loading from 'ui/common/Loading';
import PageTitle from 'ui/common/PageTitle'; import PageTitle from 'ui/common/PageTitle';
...@@ -97,7 +98,7 @@ class AccessionDetailsPage extends React.Component<IAccessionDetailsPage & WithT ...@@ -97,7 +98,7 @@ class AccessionDetailsPage extends React.Component<IAccessionDetailsPage & WithT
public render() { public render() {
const { accession, cartItems } = this.state; const { accession, cartItems } = this.state;
const { t, appConfig: { apiUrl, shoppingCart }, appConfig } = this.props; const { t, appConfig: { apiUrl, shoppingCart, map }, appConfig } = this.props;
let propertyIndex = 0; let propertyIndex = 0;
...@@ -256,6 +257,9 @@ class AccessionDetailsPage extends React.Component<IAccessionDetailsPage & WithT ...@@ -256,6 +257,9 @@ class AccessionDetailsPage extends React.Component<IAccessionDetailsPage & WithT
<Property title={ t('accession.geo.datum') } value={ details.geo.datum } index={ propertyIndex++ }/> <Property title={ t('accession.geo.datum') } value={ details.geo.datum } index={ propertyIndex++ }/>
<Property title={ t('accession.geo.uncertainty') } value={ details.geo.uncertainty } index={ propertyIndex++ }/> <Property title={ t('accession.geo.uncertainty') } value={ details.geo.uncertainty } index={ propertyIndex++ }/>
<Property title={ t('accession.geo.elevation') } value={ details.geo.elevation } index={ propertyIndex++ }/> <Property title={ t('accession.geo.elevation') } value={ details.geo.elevation } index={ propertyIndex++ }/>
{ map.enabled && details.institute && details.geo.latitude && details.geo.longitude &&
<LocationMap position={ { lat: details.geo.latitude, lng: details.geo.longitude } }/>
}
</> </>
} }
......
...@@ -5,30 +5,13 @@ import { AccessionService } from '@genesys/client/service'; ...@@ -5,30 +5,13 @@ import { AccessionService } from '@genesys/client/service';
import AccessionFilter from '@genesys/client/model/accession/AccessionFilter'; import AccessionFilter from '@genesys/client/model/accession/AccessionFilter';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Popup, Rectangle, useMapEvents } from 'react-leaflet';
const Rectangle = React.lazy(() => {
return import( /* webpackChunkName: "leaflet" */ 'react-leaflet').then((module) => {
return { default: module.Rectangle };
});
});
const Popup = React.lazy(() => {
return import( /* webpackChunkName: "leaflet" */ 'react-leaflet').then((module) => {
return { default: module.Popup };
});
});
let useMapEvents;
import( /* webpackChunkName: "leaflet" */ 'react-leaflet').then((module) => {
useMapEvents = module.useMapEvents;
});
interface ILocationMarkerProps { interface ILocationMarkerProps {
filter: AccessionFilter; filter: AccessionFilter;
} }
export default function LocationMarker(props: ILocationMarkerProps) { export default function AccessionLocationPopup(props: ILocationMarkerProps) {
const POPUP_CONTENT_LIMIT = 11; const POPUP_CONTENT_LIMIT = 11;
const { t } = useTranslation(); const { t } = useTranslation();
...@@ -44,8 +27,6 @@ export default function LocationMarker(props: ILocationMarkerProps) { ...@@ -44,8 +27,6 @@ export default function LocationMarker(props: ILocationMarkerProps) {
const currentZoom = map.getZoom(); const currentZoom = map.getZoom();
setBounds(null); setBounds(null);
// console.log('click', currentZoom, e, map);
if (currentZoom < 4) { if (currentZoom < 4) {
return; return;
} }
...@@ -95,7 +76,7 @@ export default function LocationMarker(props: ILocationMarkerProps) { ...@@ -95,7 +76,7 @@ export default function LocationMarker(props: ILocationMarkerProps) {
}); });
return ( return (
<Rectangle bounds={ bounds || [[0, 1], [0, 1]] } opacity={ bounds ? 1 : undefined }> <Rectangle bounds={ bounds || [[-900, 1800], [-900, 1800]] }>
<Popup ref={ popupRef }> <Popup ref={ popupRef }>
{ geoData && geoData.map((feature) => ( { geoData && geoData.map((feature) => (
<div key={ feature.properties.uuid }> <div key={ feature.properties.uuid }>
......
import React from 'react';
import { withTranslation, WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import AccessionMapInfo from '@genesys/client/model/accession/AccessionMapInfo';
import { WithConfig } from 'config/config';
import { MapContainer, TileLayer, Marker } from 'react-leaflet';
interface IMapComponentProps {
height?: number;
position: { lat: number, lng: number };
}
interface IMapComponentState {
mapInfo: AccessionMapInfo;
}
class LocationMap extends React.Component<IMapComponentProps & WithTranslation & WithConfig, IMapComponentState> {
public componentDidMount() {
if (typeof window !== 'undefined') {
// fix leaflet styles, won't work without it
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.leaflet-container { height: inherit; width: inherit; }';
document.getElementsByTagName('head')[0].appendChild(style);
}
}
public render() {
const { height = 400, appConfig, position } = this.props;
const { url, attribution } = appConfig.map.baseMap;
console.log('map', height);
return (
<div style={ {
height: `${height}px`, // map container must have fixed height
width: '100%',
minHeight: `${height}px`,
} }>
<MapContainer
zoom={ 6 }
scrollWheelZoom={ false }
zoomControl={ false }
dragging={ false }
doubleClickZoom={ false }
center={ position }
>
<TileLayer
attribution={ attribution }
url={ url }
/>
<Marker position={ position }><></></Marker>
</MapContainer>
</div>
);
}
}
const mapStateToProps = (state) => ({
appConfig: state.appConfig.config,
});
export default connect(mapStateToProps)(withTranslation()(LocationMap));
import React, { Suspense } from 'react';
import { withTranslation, WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import AccessionFilter from '@genesys/client/model/accession/AccessionFilter';
import { AccessionService } from '@genesys/client/service';
import AccessionMapInfo from '@genesys/client/model/accession/AccessionMapInfo';
import { WithConfig } from 'config/config';
const MapContainer = React.lazy(() => {
return import( /* webpackChunkName: "leaflet" */ 'react-leaflet').then((module) => {
return { default: module.MapContainer }; // leaflet doesn't have default export but React.lazy requires it
});
});
const TileLayer = React.lazy(() => {
return import( /* webpackChunkName: "leaflet" */ 'react-leaflet').then((module) => {
return { default: module.TileLayer };
});
});
const LocationMarker = React.lazy(() => import( /* webpackChunkName: "leaflet" */ './LocationMarker'));
interface IMapComponentProps {
filter?: AccessionFilter;
height?: number;
pageMode?: boolean;
}
interface IMapComponentState {
mapInfo: AccessionMapInfo;
}
class MapComponent extends React.Component<IMapComponentProps & WithTranslation & WithConfig, IMapComponentState> {
public static defaultLayer: {
name: 'Current selection',
default: true,
enabled: true,
opacity: 1,
color: '578218',
};
public state = {
mapInfo: null,
};
public componentDidMount() {
if (typeof window !== 'undefined') {
// fix leaflet styles, won't work without it
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.leaflet-container { height: inherit; width: inherit; }';
document.getElementsByTagName('head')[0].appendChild(style);
// load data
const { filter } = this.props;
AccessionService
.mapInfo(filter)
.then((mapInfo: AccessionMapInfo) => {
this.setState({ mapInfo });
console.log('-----', mapInfo);
})
.catch((e) => {
console.log('Error happened while loading map info', e);
})
}
}
public render() {
const { t, height = 500, filter: propFilter = null, pageMode = true, appConfig } = this.props;
const filter = propFilter || appConfig.filter;
const { url, attribution } = appConfig.map.baseMap;
const { mapInfo } = this.state;
const layerUrl = `{s}/acn/tile/{z}/{x}/{y}?f=${mapInfo && mapInfo.filterCode ? mapInfo.filterCode : ''}`;
if (mapInfo && mapInfo.bounds) {
if (!mapInfo.bounds[0] || !mapInfo.bounds[1] || !mapInfo.bounds[0][0] || !mapInfo.bounds[0][1] || !mapInfo.bounds[1][0] || !mapInfo.bounds[1][1]) {
mapInfo.bounds = [[-170, 80], [170, -80]];
}
}
return (
<div style={ {
height: pageMode ? 'calc(100vh - 115px)' : `${height}px`, // map container must have fixed height
width: '100%',
minHeight: `${height}px`,
} }>
<Suspense fallback={ <div>{ t('loading') }</div> }>
{ mapInfo &&
<MapContainer
bounds={ mapInfo.bounds }
zoom={ 4 }
// scrollWheelZoom={ scrollWheelZoom }
minZoom={ 2 }
maxZoom={ 14 }
>
<TileLayer
attribution={ attribution }
url={ url }
/>
<TileLayer
{ ...MapComponent.defaultLayer }
zIndex={ 2 }
updateInterval={ 1000 }
updateWhenZooming={ false }
attribution="&amp;copy Accession localities from <a href=&quot;/&quot;>Genesys PGR</a>"
url={ layerUrl }
subdomains={ mapInfo.tileServers }
/>
{ pageMode && (
<Suspense fallback={ <div>{ t('loading') }</div> }>
<LocationMarker filter={ filter }/>
</Suspense>
) }
</MapContainer>
}
</Suspense>
</div>
);
}
}
const mapStateToProps = (state) => ({
appConfig: state.appConfig.config,
});
export default connect(mapStateToProps)(withTranslation()(MapComponent));
import * as React from 'react'; import React from 'react';
// import { WithTranslation, withTranslation } from 'react-i18next'; import { withTranslation, WithTranslation } from 'react-i18next';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import MapComponent from './MapComponent';
import AccessionFilter from '@genesys/client/model/accession/AccessionFilter';
interface IMapPageProps { import { AccessionService } from '@genesys/client/service';
filter: AccessionFilter; import AccessionMapInfo from '@genesys/client/model/accession/AccessionMapInfo';
import { WithConfig } from 'config/config';
import AccessionLocationPopup from 'map/AccessionLocationPopup';
import { MapContainer, TileLayer } from 'react-leaflet';
interface IMapComponentState {
mapInfo: AccessionMapInfo;
} }
class MapPage extends React.Component<IMapPageProps, any> { class MapComponent extends React.Component<WithTranslation & WithConfig, IMapComponentState> {
public static defaultLayer: {
name: 'Current selection',
default: true,
enabled: true,
opacity: 1,
color: '578218',
};
public state = {
mapInfo: null,
};
// private mapRef = null; public componentDidMount() {
// private rectRef = null; if (typeof window !== 'undefined') {
// fix leaflet styles, won't work without it
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.leaflet-container { height: inherit; width: inherit; }';
document.getElementsByTagName('head')[0].appendChild(style);
public state = {}; // load data
const { appConfig } = this.props;
public constructor(props, context) { AccessionService
super(props, context); .mapInfo(appConfig.filter)
.then((mapInfo: AccessionMapInfo) => {
this.setState({ mapInfo });
})
.catch((e) => {
console.log('Error happened while loading map info', e);
});
}
} }
public render() { public render() {
const { filter } = this.props; const { appConfig } = this.props;
const { url, attribution } = appConfig.map.baseMap;
const { mapInfo } = this.state;
const layerUrl = `{s}/acn/tile/{z}/{x}/{y}?f=${mapInfo && mapInfo.filterCode ? mapInfo.filterCode : ''}`;
if (mapInfo && mapInfo.bounds) {
if (!mapInfo.bounds[0] || !mapInfo.bounds[1] || !mapInfo.bounds[0][0] || !mapInfo.bounds[0][1] || !mapInfo.bounds[1][0] || !mapInfo.bounds[1][1]) {
mapInfo.bounds = [[-170, 80], [170, -80]];
}
}
return ( return (
<MapComponent filter={ filter }/> <div style={ {
height: 'calc(100vh - 115px)', // map container must have fixed height
width: '100%',
minHeight: '500px',
} }>
{ mapInfo &&
<MapContainer
bounds={ mapInfo.bounds }
zoom={ 4 }
scrollWheelZoom={ true }
minZoom={ 2 }
maxZoom={ 14 }
>
<TileLayer
attribution={ attribution }
url={ url }
/>
<TileLayer
{ ...MapComponent.defaultLayer }
zIndex={ 2 }
updateInterval={ 1000 }
updateWhenZooming={ false }
attribution="&amp;copy Accession localities from <a href=&quot;/&quot;>Genesys PGR</a>"
url={ layerUrl }
subdomains={ mapInfo.tileServers }
/>
<AccessionLocationPopup filter={ appConfig.filter }/>
</MapContainer>
}
</div>
); );
} }
} }
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
filter: state.appConfig.config.filter, appConfig: state.appConfig.config,
}); });
export default connect(mapStateToProps)(MapPage); export default connect(mapStateToProps)(withTranslation()(MapComponent));
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment