diff --git a/src/client/actions/index.js b/src/client/actions/index.js index 505703025f3763499ae4d5bc333f058a00af5805..f56bcfc9438d3b9d605834f45dd9742c073cddb1 100644 --- a/src/client/actions/index.js +++ b/src/client/actions/index.js @@ -318,8 +318,9 @@ export const animateMap = value => ({ type: ANIMATE_MAP, value }) -export const updateMapBounds = bounds => ({ +export const updateMapBounds = ({ resultClass, bounds }) => ({ type: UPDATE_MAP_BOUNDS, + resultClass, bounds }) export const fetchGeoJSONLayers = ({ layerIDs, bounds }) => ({ diff --git a/src/client/components/facet_bar/FacetBar.js b/src/client/components/facet_bar/FacetBar.js index 8f98cea62d6900c97f7d42a688a2901571356d02..c86efb363c84a6e00fa6dd9c1ac4f98f88a0257c 100644 --- a/src/client/components/facet_bar/FacetBar.js +++ b/src/client/components/facet_bar/FacetBar.js @@ -309,11 +309,10 @@ class FacetBar extends React.Component { />} {facetedSearchMode === 'clientFS' && <LeafletMapDialog - map={this.props.leafletMap} + clientFSState={this.props.clientFSState} clientFSFetchResults={this.props.clientFSFetchResults} clientFSClearResults={this.props.clientFSClearResults} updateMapBounds={this.props.updateMapBounds} - fetching={this.props.clientFS.spatialResultsFetching} showError={this.props.showError} perspectiveID={facetClass} />} diff --git a/src/client/components/facet_bar/LeafletMapDialog.js b/src/client/components/facet_bar/LeafletMapDialog.js index 294b9038dc301e73221f7ce492b0037bcc9a7cab..64c137b1888446755d354f37d80e09ff1d4a4183 100644 --- a/src/client/components/facet_bar/LeafletMapDialog.js +++ b/src/client/components/facet_bar/LeafletMapDialog.js @@ -55,7 +55,7 @@ class LeafletMapDialog extends React.Component { }; handleSearchByArea = () => { - if (this.props.map.zoomLevel > 10) { + if (this.props.clientFSState.maps.clientFSBboxSearch.zoom > 10) { this.props.clientFSClearResults() this.props.clientFSFetchResults({ jenaIndex: 'spatial' }) this.setState({ open: false }) @@ -68,7 +68,9 @@ class LeafletMapDialog extends React.Component { } render () { - const { classes, perspectiveID } = this.props + const { classes, clientFSState, perspectiveID } = this.props + const { maps, spatialResultsFetching } = clientFSState + const { center, zoom } = maps.clientFSBboxSearch return ( <Paper className={classes.root}> @@ -80,7 +82,7 @@ class LeafletMapDialog extends React.Component { onClick={this.handleClickOpen} > {intl.get(`perspectives.${perspectiveID}.searchByArea`)} - {this.props.fetching + {spatialResultsFetching ? <CircularProgress className={classes.rightIcon} color='inherit' size={24} /> : <CropFreeIcon className={classes.rightIcon} />} </Button> @@ -97,8 +99,9 @@ class LeafletMapDialog extends React.Component { > <DialogTitle id='dialog-title'>{intl.get(`perspectives.${perspectiveID}.searchByAreaTitle`)}</DialogTitle> <LeafletMap - center={[65.184809, 27.314050]} - zoom={5} + center={center} + zoom={zoom} + resultClass='clientFSBboxSearch' pageType='clientFSResults' showMapModeControl={false} showInstanceCountInClusters={false} @@ -124,14 +127,11 @@ class LeafletMapDialog extends React.Component { LeafletMapDialog.propTypes = { classes: PropTypes.object.isRequired, - strings: PropTypes.object, - map: PropTypes.object.isRequired, - getGeoJSON: PropTypes.func, - updateMapBounds: PropTypes.func, + clientFSState: PropTypes.object.isRequired, clientFSFetchResults: PropTypes.func.isRequired, clientFSClearResults: PropTypes.func.isRequired, + updateMapBounds: PropTypes.func, showError: PropTypes.func, - fetching: PropTypes.bool, perspectiveID: PropTypes.string.isRequired } diff --git a/src/client/components/facet_results/LeafletMap.js b/src/client/components/facet_results/LeafletMap.js index 98f92e0dbf1ef69322ce5c2f4f11c0642232327f..d65b9299c4d2b0e99151e0a6cc5283a36ebe774c 100644 --- a/src/client/components/facet_results/LeafletMap.js +++ b/src/client/components/facet_results/LeafletMap.js @@ -104,8 +104,7 @@ class LeafletMap extends React.Component { prevZoomLevel: null, enlargedBounds: null, mapMode: props.mapMode, - showBuffer: true, - initFinished: false + showBuffer: true } } @@ -126,7 +125,6 @@ class LeafletMap extends React.Component { if (this.props.showExternalLayers && !this.props.locateUser) { this.maybeUpdateEnlargedBoundsAndFetchGeoJSONLayers({ eventType: 'programmatic' }) } - this.setState({ initFinished: true }) } componentDidUpdate = (prevProps, prevState) => { @@ -262,15 +260,6 @@ class LeafletMap extends React.Component { if (prevProps.infoHeaderExpanded && (prevProps.infoHeaderExpanded !== this.props.infoHeaderExpanded)) { this.leafletMap.invalidateSize() } - - if (this.state.initFinished) { - if (this.props.layerControlExpanded) { - this.leafletMap.invalidateSize() - // this.layerControl.collapse() - // this.layerControl.expand() - } - this.setState({ initFinished: null }) - } } initMap = () => { @@ -364,13 +353,18 @@ class LeafletMap extends React.Component { } if (this.props.updateMapBounds) { - this.props.updateMapBounds(this.boundsToValues()) - this.leafletMap.on('moveend', () => { - this.props.updateMapBounds(this.boundsToValues()) - }) + this.updateMapBounds() + this.leafletMap.on('moveend', () => this.updateMapBounds()) } } + updateMapBounds = () => { + this.props.updateMapBounds({ + resultClass: this.props.resultClass, + bounds: this.boundsToObject() + }) + } + setCustomMapControlVisibility = () => { const { activeLayers } = this.state let hideCustomControl = true @@ -418,7 +412,7 @@ class LeafletMap extends React.Component { this.maybeUpdateEnlargedBoundsAndFetchGeoJSONLayers({ eventType: 'programmatic' }) } - boundsToValues = () => { + boundsToObject = () => { const bounds = this.leafletMap.getBounds() const latMin = bounds._southWest.lat const longMin = bounds._southWest.lng @@ -429,6 +423,7 @@ class LeafletMap extends React.Component { longMin: longMin, latMax: latMax, longMax: longMax, + center: this.leafletMap.getCenter(), zoom: this.leafletMap.getZoom() } } @@ -581,7 +576,7 @@ class LeafletMap extends React.Component { } const safeFunc = eventType === 'zoomend' ? this.isSafeToLoadLargeLayersAfterZooming : this.isSafeToLoadLargeLayers - if (this.props.layers.fetching || this.state.activeLayers.length < 0 || !safeFunc()) { + if (this.props.leafletMapState.fetching || this.state.activeLayers.length < 0 || !safeFunc()) { return } // console.log('setting new enlarged bounds') diff --git a/src/client/components/facet_results/VirtualizedTable.js b/src/client/components/facet_results/VirtualizedTable.js index 485f11cf18022ac4212b241de96ac5a72146919a..bed03ebd5d739f1bb46a4f56ee241f14f8c8684e 100644 --- a/src/client/components/facet_results/VirtualizedTable.js +++ b/src/client/components/facet_results/VirtualizedTable.js @@ -161,8 +161,8 @@ class VirtualizedTable extends React.PureComponent { rowGetter={rowGetter} rowCount={this.props.list.size} sort={this._sort} - sortBy={this.props.clientFS.sortBy} - sortDirection={this.props.clientFS.sortDirection.toUpperCase()} + sortBy={this.props.clientFSState.sortBy} + sortDirection={this.props.clientFSState.sortDirection.toUpperCase()} width={width} height={height} headerHeight={50} @@ -284,7 +284,7 @@ class VirtualizedTable extends React.PureComponent { VirtualizedTable.propTypes = { classes: PropTypes.object.isRequired, list: PropTypes.instanceOf(Immutable.List).isRequired, - clientFS: PropTypes.object, + clientFSState: PropTypes.object, clientFSSortResults: PropTypes.func, perspectiveID: PropTypes.string.isRequired } diff --git a/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js b/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js index 993f711e3c9f2faaaa19e89b5b48a941706d674c..a381f1b0ef426a58f7231c5f164f6ac61eb4a118 100644 --- a/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js +++ b/src/client/components/perspectives/sampo/client_fs/ClientFSPerspective.js @@ -13,7 +13,10 @@ import Footer from '../Footer' import { createPopUpContentNameSampo, layerConfigs } from '../../../../configs/sampo/Leaflet/LeafletConfig' const ClientFSPerspective = props => { - const { rootUrl, perspective, screenSize } = props + const { rootUrl, perspective, screenSize, clientFSState } = props + const { maps } = clientFSState + const { clientFSMapClusters, clientFSMapMarkers } = maps + // console.log(clientFSMapClusters) const layerControlExpanded = screenSize === 'md' || screenSize === 'lg' || screenSize === 'xl' @@ -33,7 +36,7 @@ const ClientFSPerspective = props => { render={() => <VirtualizedTable list={Immutable.List(props.clientFSResults)} - clientFS={props.clientFS} + clientFSState={props.clientFSState} clientFSSortResults={props.clientFSSortResults} perspectiveID={perspective.id} />} @@ -42,10 +45,11 @@ const ClientFSPerspective = props => { path={`${rootUrl}/${perspective.id}/federated-search/map_clusters`} render={() => <LeafletMap - center={[65.184809, 27.314050]} - zoom={5} + center={clientFSMapClusters.center} + zoom={clientFSMapClusters.zoom} results={props.clientFSResults} leafletMapState={props.leafletMap} + resultClass='clientFSMapClusters' pageType='clientFSResults' mapMode='cluster' createPopUpContent={createPopUpContentNameSampo} @@ -60,20 +64,22 @@ const ClientFSPerspective = props => { showExternalLayers layerControlExpanded={layerControlExpanded} layerConfigs={layerConfigs} + updateMapBounds={props.updateMapBounds} />} /> <Route path={`${rootUrl}/${perspective.id}/federated-search/map_markers`} render={() => { - if (props.clientFSResults.length > 3000) { + if (props.clientFSResults.length > 500) { return <ResultInfo message={intl.get('leafletMap.tooManyResults')} /> } else { return ( <LeafletMap - center={[65.184809, 27.314050]} - zoom={5} + center={clientFSMapMarkers.center} + zoom={clientFSMapMarkers.zoom} results={props.clientFSResults} leafletMapState={props.leafletMap} + resultClass='clientFSMapMarkers' pageType='clientFSResults' mapMode='marker' createPopUpContent={createPopUpContentNameSampo} @@ -88,6 +94,7 @@ const ClientFSPerspective = props => { showExternalLayers layerControlExpanded={layerControlExpanded} layerConfigs={layerConfigs} + updateMapBounds={props.updateMapBounds} /> ) } @@ -117,10 +124,11 @@ ClientFSPerspective.propTypes = { routeProps: PropTypes.object.isRequired, perspective: PropTypes.object.isRequired, screenSize: PropTypes.string.isRequired, - clientFS: PropTypes.object.isRequired, + clientFSState: PropTypes.object.isRequired, clientFSResults: PropTypes.array, clientFSSortResults: PropTypes.func.isRequired, leafletMap: PropTypes.object.isRequired, + updateMapBounds: PropTypes.func.isRequired, fetchGeoJSONLayers: PropTypes.func, fetchGeoJSONLayersBackend: PropTypes.func.isRequired, clearGeoJSONLayers: PropTypes.func.isRequired, diff --git a/src/client/containers/SemanticPortal.js b/src/client/containers/SemanticPortal.js index b63aa0cab527f0b4332e3628557214cfec8e2331..8c21861dc6964ed11d792145ee318031306dd1e2 100644 --- a/src/client/containers/SemanticPortal.js +++ b/src/client/containers/SemanticPortal.js @@ -269,7 +269,7 @@ const SemanticPortal = props => { if (lgScreen) { screenSize = 'lg' } if (xlScreen) { screenSize = 'xl' } const rootUrlWithLang = `${rootUrl}/${props.options.currentLocale}` - const noResults = props.clientFS.results == null + const noResults = props.clientFSState.results == null useEffect(() => { document.title = intl.get('html.title') @@ -539,11 +539,11 @@ const SemanticPortal = props => { facetedSearchMode='clientFS' facetClass='clientFSPlaces' resultClass='clientFSPlaces' - facetData={props.clientFS} + facetData={props.clientFSState} clientFSFacetValues={props.clientFSFacetValues} - fetchingResultCount={props.clientFS.textResultsFetching} - resultCount={noResults ? 0 : props.clientFS.results.length} - clientFS={props.clientFS} + fetchingResultCount={props.clientFSState.textResultsFetching} + resultCount={noResults ? 0 : props.clientFSState.results.length} + clientFSState={props.clientFSState} clientFSToggleDataset={props.clientFSToggleDataset} clientFSFetchResults={props.clientFSFetchResults} clientFSClearResults={props.clientFSClearResults} @@ -564,10 +564,11 @@ const SemanticPortal = props => { routeProps={routeProps} perspective={perspectiveConfig.find(p => p.id === 'clientFSPlaces')} screenSize={screenSize} - clientFS={props.clientFS} + clientFSState={props.clientFSState} clientFSResults={props.clientFSResults} clientFSSortResults={props.clientFSSortResults} leafletMap={props.leafletMap} + updateMapBounds={props.updateMapBounds} fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend} fetchGeoJSONLayers={props.fetchGeoJSONLayers} clearGeoJSONLayers={props.clearGeoJSONLayers} @@ -641,7 +642,7 @@ const mapStateToProps = state => { emloActorsFacetsConstrainSelf: state.emloActorsFacetsConstrainSelf, leafletMap: state.leafletMap, fullTextSearch: state.fullTextSearch, - clientFS: state.clientSideFacetedSearch, + clientFSState: state.clientSideFacetedSearch, clientFSResults, clientFSFacetValues, animationValue: state.animation.value, diff --git a/src/client/epics/index.js b/src/client/epics/index.js index 1dfab355779ab6455ce2a07a681e5456caa9ee90..d37b12fa4d8d6524749c2869f78517c927126bfa 100644 --- a/src/client/epics/index.js +++ b/src/client/epics/index.js @@ -386,13 +386,14 @@ const clientFSFetchResultsEpic = (action$, state$) => action$.pipe( debounceTime(500), switchMap(([action, state]) => { const { jenaIndex } = action - const selectedDatasets = pickSelectedDatasets(state.clientSideFacetedSearch.datasets) + const { clientSideFacetedSearch } = state + const selectedDatasets = pickSelectedDatasets(clientSideFacetedSearch.datasets) const dsParams = selectedDatasets.map(ds => `dataset=${ds}`).join('&') let requestUrl if (action.jenaIndex === 'text') { requestUrl = `${apiUrl}/federated-search?q=${action.query}&${dsParams}` } else if (action.jenaIndex === 'spatial') { - const { latMin, longMin, latMax, longMax } = state.leafletMap + const { latMin, longMin, latMax, longMax } = clientSideFacetedSearch.maps.clientFSBboxSearch requestUrl = `${apiUrl}/federated-search?latMin=${latMin}&longMin=${longMin}&latMax=${latMax}&longMax=${longMax}&${dsParams}` } return ajax.getJSON(requestUrl).pipe( diff --git a/src/client/reducers/general/helpers.js b/src/client/reducers/general/helpers.js index 72058c363c92d9a2cae6bcc126560bd7dabc2877..56a2a4aec06d46dcb5ed2218d4e4062a5319fca0 100644 --- a/src/client/reducers/general/helpers.js +++ b/src/client/reducers/general/helpers.js @@ -323,3 +323,21 @@ export const updateKnowledgeGraphMetadata = (state, action) => { fetching: false } } + +export const updateMapBounds = (state, action) => { + const { resultClass, bounds } = action + return { + ...state, + maps: { + ...state.maps, + [resultClass]: { + latMin: bounds.latMin, + longMin: bounds.longMin, + latMax: bounds.latMax, + longMax: bounds.longMax, + center: bounds.center, + zoom: bounds.zoom + } + } + } +} diff --git a/src/client/reducers/general/leafletMap.js b/src/client/reducers/general/leafletMap.js index 3c894bfb0a528313bf20254e0e22564ddfe1bfe2..849aca9d3a6db8ebf4e9dde2b2dffbd8c697be86 100644 --- a/src/client/reducers/general/leafletMap.js +++ b/src/client/reducers/general/leafletMap.js @@ -3,19 +3,13 @@ import { FETCH_GEOJSON_LAYERS_BACKEND, CLEAR_GEOJSON_LAYERS, UPDATE_GEOJSON_LAYERS, - UPDATE_MAP_BOUNDS, FETCH_GEOJSON_LAYERS_FAILED } from '../../actions' export const INITIAL_STATE = { layerData: [], updateID: 0, - fetching: false, - latMin: 0, - longMin: 0, - latMax: 0, - longMax: 0, - zoomLevel: 4 + fetching: false } const leafletMap = (state = INITIAL_STATE, action) => { @@ -43,15 +37,6 @@ const leafletMap = (state = INITIAL_STATE, action) => { ...state, layerData: [] } - case UPDATE_MAP_BOUNDS: - return { - ...state, - latMin: action.bounds.latMin, - longMin: action.bounds.longMin, - latMax: action.bounds.latMax, - longMax: action.bounds.longMax, - zoomLevel: action.bounds.zoom - } default: return state } diff --git a/src/client/reducers/general/results.js b/src/client/reducers/general/results.js index fa4668a1ca3ba6ac55547598fd52035559a1fce2..6b798d2361f6ce849bceb0c5b3c0fd3df7773a25 100644 --- a/src/client/reducers/general/results.js +++ b/src/client/reducers/general/results.js @@ -16,7 +16,8 @@ import { UPDATE_ROWS_PER_PAGE, SORT_RESULTS, UPDATE_PERSPECTIVE_HEADER_EXPANDED, - UPDATE_KNOWLEDGE_GRAPH_METADATA + UPDATE_KNOWLEDGE_GRAPH_METADATA, + UPDATE_MAP_BOUNDS } from '../../actions' import { fetchResults, @@ -33,7 +34,8 @@ import { updatePage, updateRowsPerPage, updateHeaderExpanded, - updateKnowledgeGraphMetadata + updateKnowledgeGraphMetadata, + updateMapBounds } from './helpers' export const handleDataFetchingAction = (state, action) => { @@ -71,6 +73,8 @@ export const handleDataFetchingAction = (state, action) => { return updateHeaderExpanded(state, action) case UPDATE_KNOWLEDGE_GRAPH_METADATA: return updateKnowledgeGraphMetadata(state, action) + case UPDATE_MAP_BOUNDS: + return updateMapBounds(state, action) default: return state } diff --git a/src/client/reducers/sampo/clientSideFacetedSearch.js b/src/client/reducers/sampo/clientSideFacetedSearch.js index 2f4b8f86f20450fa959e3152503b13099fc8654e..3f4be413c3cba8600ee34e14796b472b565a75b7 100644 --- a/src/client/reducers/sampo/clientSideFacetedSearch.js +++ b/src/client/reducers/sampo/clientSideFacetedSearch.js @@ -6,8 +6,10 @@ import { CLIENT_FS_UPDATE_RESULTS, CLIENT_FS_CLEAR_RESULTS, CLIENT_FS_UPDATE_FACET, - CLIENT_FS_SORT_RESULTS + CLIENT_FS_SORT_RESULTS, + UPDATE_MAP_BOUNDS } from '../../actions' +import { handleDataFetchingAction } from '../general/results' export const INITIAL_STATE = { query: '', @@ -94,7 +96,21 @@ export const INITIAL_STATE = { groupBy: 'broaderTypeLabel', groupByLabel: 'Paikanlaji', textResultsFetching: false, - spatialResultsFetching: false + spatialResultsFetching: false, + maps: { + clientFSBboxSearch: { + center: [65.184809, 27.314050], + zoom: 5 + }, + clientFSMapClusters: { + center: [65.184809, 27.314050], + zoom: 5 + }, + clientFSMapMarkers: { + center: [65.184809, 27.314050], + zoom: 5 + } + } } const clientSideFacetedSearch = (state = INITIAL_STATE, action) => { @@ -131,7 +147,13 @@ const clientSideFacetedSearch = (state = INITIAL_STATE, action) => { results: null, fetchingResults: false, query: INITIAL_STATE.query, - facets: INITIAL_STATE.facets + facets: INITIAL_STATE.facets, + maps: { + ...state.maps, + // reset center and zoom for maps that are used for results: + clientFSMapClusters: INITIAL_STATE.maps.clientFSMapClusters, + clientFSMapMarkers: INITIAL_STATE.maps.clientFSMapMarkers + } } case CLIENT_FS_UPDATE_RESULTS: return { @@ -147,11 +169,23 @@ const clientSideFacetedSearch = (state = INITIAL_STATE, action) => { sortBy: action.options.sortBy, sortDirection: action.options.sortDirection } + case UPDATE_MAP_BOUNDS: + if (resultClassesForMapBounds.has(action.resultClass)) { + return handleDataFetchingAction(state, action) + } else { + return state + } default: return state } } +const resultClassesForMapBounds = new Set([ + 'clientFSBboxSearch', + 'clientFSMapClusters', + 'clientFSMapMarkers' +]) + const clientFSUpdateFacet = (state, action) => { const { facetID, value, latestValues } = action const newSelectionsSet = state.facets[facetID].selectionsSet diff --git a/src/client/translations/sampo/localeEN.json b/src/client/translations/sampo/localeEN.json index 15b111a2801614fa2b2cbb135654baf2fc91e858..6fd49067828f745831f6c65fc1bc08ea2c2c3ae2 100644 --- a/src/client/translations/sampo/localeEN.json +++ b/src/client/translations/sampo/localeEN.json @@ -150,7 +150,7 @@ }, "wrongZoomLevel": "The map zoom level has to at least 11", "wrongZoomLevelFHA": "The map zoom level has to be at least 13 in order to show this layer", - "tooManyResults": "More than 3000 results, please use clustered map or heatmap" + "tooManyResults": "More than 500 results, please use clustered map or heatmap" }, "instancePageGeneral": { "introduction": "<p class=\"MuiTypography-root MuiTypography-body1 MuiTypography-paragraph\"> This landing page provides a human-readable summary of the data points that link to this {entity}. The data included in this summary reflect only those data points used in the MMM Portal. Click the Open in Linked Data Browser on button on the Export tab to view the complete set of classes and properties linked to this record. </p> <p class=\"MuiTypography-root MuiTypography-body1 MuiTypography-paragraph\"> To cite this record, use its url. You can use also use the url to return directly to the record at any time. </p>",