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
enabled: true, // Enable shopping cart
},
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
......@@ -70,6 +77,7 @@ to *@geneesys-pgr/ui-embedded*:
|`title`| *Genesys:* | HTML page title prefix |
| `accession` | `{ ... }` | See *Accession* configuration options |
| `shoppingCart` | `{ ... }` | See *Shopping cart* configuration options |
| `map` | `{ ... }` | See *Map* configuration |
Additional configuration options are described below.
......@@ -103,3 +111,18 @@ const genesysConfig = {
|---|---|---|
|`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 = {
noEmitOnErrors: true,
splitChunks: {
chunks: 'all',
// cacheGroups: {
// deps: {
// test: /[\\/]node_modules[\\/]/,
// name: 'deps',
// chunks: 'all'
// },
// },
cacheGroups: {
deps: {
test: /[\\/]node_modules[\\/]/,
name: 'deps',
chunks: 'all'
},
},
},
},
};
......@@ -34,6 +34,9 @@
<li class="nav-item">
<a class="nav-link" href="#/cart">Cart</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#/map">Map</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.html">EN</a>
</li>
......
......@@ -10,6 +10,7 @@ import { WithConfig } from 'config/config';
import { CountryName } from 'ui/common/CountryName';
import GrinSpecies from 'ui/common/GrinSpecies';
import PdciTable from 'ui/common/PdciTable';
import LocationMap from 'map/LocationMap';
import Loading from 'ui/common/Loading';
import PageTitle from 'ui/common/PageTitle';
......@@ -97,7 +98,7 @@ class AccessionDetailsPage extends React.Component<IAccessionDetailsPage & WithT
public render() {
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;
......@@ -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.uncertainty') } value={ details.geo.uncertainty } 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';
import AccessionFilter from '@genesys/client/model/accession/AccessionFilter';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
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;
});
import { Popup, Rectangle, useMapEvents } from 'react-leaflet';
interface ILocationMarkerProps {
filter: AccessionFilter;
}
export default function LocationMarker(props: ILocationMarkerProps) {
export default function AccessionLocationPopup(props: ILocationMarkerProps) {
const POPUP_CONTENT_LIMIT = 11;
const { t } = useTranslation();
......@@ -44,8 +27,6 @@ export default function LocationMarker(props: ILocationMarkerProps) {
const currentZoom = map.getZoom();
setBounds(null);
// console.log('click', currentZoom, e, map);
if (currentZoom < 4) {
return;
}
......@@ -95,7 +76,7 @@ export default function LocationMarker(props: ILocationMarkerProps) {
});
return (
<Rectangle bounds={ bounds || [[0, 1], [0, 1]] } opacity={ bounds ? 1 : undefined }>
<Rectangle bounds={ bounds || [[-900, 1800], [-900, 1800]] }>
<Popup ref={ popupRef }>
{ geoData && geoData.map((feature) => (
<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 { WithTranslation, withTranslation } from 'react-i18next';
import React from 'react';
import { withTranslation, WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import MapComponent from './MapComponent';
import AccessionFilter from '@genesys/client/model/accession/AccessionFilter';
interface IMapPageProps {
filter: AccessionFilter;
import { AccessionService } from '@genesys/client/service';
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;
// private rectRef = 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);
public state = {};
// load data
const { appConfig } = this.props;
public constructor(props, context) {
super(props, context);
AccessionService
.mapInfo(appConfig.filter)
.then((mapInfo: AccessionMapInfo) => {
this.setState({ mapInfo });
})
.catch((e) => {
console.log('Error happened while loading map info', e);
});
}
}
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 (
<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) => ({
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