From db9e7b3e361406f330fde4c3c42689c23cc284ed Mon Sep 17 00:00:00 2001 From: esikkala <esko.ikkala@aalto.fi> Date: Fri, 30 Nov 2018 14:51:05 +0200 Subject: [PATCH] Creation places map: rename to production places, add support for bodley places --- src/client/components/Deck.js | 1 - src/client/components/LeafletMap.js | 69 ++++++++++++++++++++-------- src/client/components/Manuscripts.js | 6 +-- src/client/components/ResultTable.js | 2 +- src/client/components/ViewTabs.js | 6 +-- src/client/containers/MapApp.js | 2 - src/client/epics/index.js | 2 +- src/server/index.js | 4 +- src/server/sparql/Datasets.js | 65 +++++++++++++++++--------- src/server/sparql/Manuscripts.js | 2 +- 10 files changed, 103 insertions(+), 56 deletions(-) diff --git a/src/client/components/Deck.js b/src/client/components/Deck.js index 6a4aba70..616c9c9b 100644 --- a/src/client/components/Deck.js +++ b/src/client/components/Deck.js @@ -208,7 +208,6 @@ class Deck extends React.Component { <NavigationControl onViewportChange={this._onViewportChange} /> </div> {this._renderSpinner()} - <InfoDialog open={this.state.dialog.open} onClose={this.closeDialog.bind(this)} diff --git a/src/client/components/LeafletMap.js b/src/client/components/LeafletMap.js index 78ae78d0..cf7d0a91 100644 --- a/src/client/components/LeafletMap.js +++ b/src/client/components/LeafletMap.js @@ -1,8 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; import L from 'leaflet'; import { has, orderBy } from 'lodash'; // import LeafletSidebar from './LeafletSidebar'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import { purple } from '@material-ui/core/colors'; import 'leaflet-sidebar-v2/js/leaflet-sidebar.min.js'; import 'leaflet-sidebar-v2/css/leaflet-sidebar.min.css'; @@ -32,6 +35,18 @@ const style = { height: '100%' }; +const styles = () => ({ + spinner: { + height: 40, + width: 40, + position: 'absolute', + left: '50%', + top: '50%', + transform: 'translate(-50%,-50%)', + zIndex: 500 + }, +}); + // https://github.com/pointhi/leaflet-color-markers // const ColorIcon = L.Icon.extend({ // options: { @@ -46,7 +61,7 @@ const style = { class LeafletMap extends React.Component { componentDidMount() { - this.props.fetchPlaces('creationPlaces'); + this.props.fetchPlaces(this.props.variant); // Base layers // const OSMBaseLayer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { @@ -128,7 +143,7 @@ class LeafletMap extends React.Component { } if (this.props.place !== place) { - this.markers[this.props.place.id.replace('http://ldf.fi/mmm/place/', '')] + this.markers[this.props.place.id] .bindPopup(this.createPopUpContent(this.props.place), { maxHeight: 300, maxWidth: 400, @@ -140,13 +155,24 @@ class LeafletMap extends React.Component { } + renderSpinner() { + if(this.props.fetchingPlaces) { + return ( + <div className={this.props.classes.spinner}> + <CircularProgress style={{ color: purple[500] }} thickness={5} /> + </div> + ); + } + return null; + } + updateMarkers(results) { this.resultMarkerLayer.clearLayers(); this.markers = {}; Object.values(results).forEach(value => { const marker = this.createMarker(value); - this.markers[value.id.replace('http://ldf.fi/mmm/place/', '')] = marker; + this.markers[value.id] = marker; marker == null ? null : marker.addTo(this.resultMarkerLayer); }); } @@ -174,7 +200,7 @@ class LeafletMap extends React.Component { }); results.forEach(value => { const marker = this.createMarker(value); - this.markers[value.id.replace('http://ldf.fi/mmm/place/', '')] = marker; + this.markers[value.id] = marker; marker == null ? null : clusterer.addLayer(marker); }); clusterer.addTo(this.resultMarkerLayer); @@ -207,17 +233,16 @@ class LeafletMap extends React.Component { } } - markerOnClick = (event) => { - const placeId = (event.target.options.id.replace('http://ldf.fi/mmm/place/', '')); - this.props.fetchPlace(placeId); + markerOnClick = event => { + this.props.fetchPlace(event.target.options.id); }; createPopUpContent(result) { - let popUpTemplate = `<h3><a target="_blank" rel="noopener noreferrer" href=${result.sdbmLink}>${result.prefLabel}</a></p></h3>`; - if (has(result, 'source')) { - popUpTemplate += `<p>Place authority: <a target="_blank" rel="noopener noreferrer" href=${result.source}>${result.source}</a></p>`; + let popUpTemplate = `<h3><a target="_blank" rel="noopener noreferrer" href=${result.dataProviderUrl}>${result.prefLabel}</a></p></h3>`; + if (has(result, 'sameAs')) { + popUpTemplate += `<p>Place authority: <a target="_blank" rel="noopener noreferrer" href=${result.sameAs}>${result.sameAs}</a></p>`; } - popUpTemplate += `<p>Manuscripts created here:</p>`; + popUpTemplate += `<p>Manuscripts produced here:</p>`; popUpTemplate += this.createManscriptListing(result.manuscript); return popUpTemplate; } @@ -228,13 +253,11 @@ class LeafletMap extends React.Component { manuscripts = orderBy(manuscripts, 'id'); html += '<ul>'; manuscripts.forEach(ms => { - let sdbmLink = has(ms, 'manuscriptRecord') ? ms.manuscriptRecord : ms.entry; - html += '<li><a target="_blank" rel="noopener noreferrer" href=' + sdbmLink + '>' + sdbmLink + '</a></li>'; + html += '<li><a target="_blank" rel="noopener noreferrer" href=' + ms.dataProviderUrl + '>' + ms.dataProviderUrl + '</a></li>'; }); html += '</ul>'; } else { - let sdbmLink = has(manuscripts, 'manuscriptRecord') ? manuscripts.manuscriptRecord : manuscripts.entry; - html += '<p><a target="_blank" rel="noopener noreferrer" href=' + sdbmLink + '>' + sdbmLink + '</a></p>'; + html += '<p><a target="_blank" rel="noopener noreferrer" href=' + manuscripts.dataProviderUrl + '>' + manuscripts.dataProviderUrl + '</a></p>'; } return html; } @@ -260,20 +283,26 @@ class LeafletMap extends React.Component { render() { return ( - <div className="leaflet-outer-container"> - {/*<LeafletSidebar />*/} - <div id="map" style={style} /> - </div> + <React.Fragment> + <div className="leaflet-outer-container"> + {/*<LeafletSidebar />*/} + <div id="map" style={style} /> + </div> + {this.renderSpinner()} + </React.Fragment> ); } } LeafletMap.propTypes = { + classes: PropTypes.object.isRequired, fetchPlaces: PropTypes.func.isRequired, + fetchingPlaces: PropTypes.bool.isRequired, fetchPlace: PropTypes.func.isRequired, results: PropTypes.array.isRequired, mapMode: PropTypes.string.isRequired, + variant: PropTypes.string.isRequired, place: PropTypes.object.isRequired, }; -export default LeafletMap; +export default withStyles(styles)(LeafletMap); diff --git a/src/client/components/Manuscripts.js b/src/client/components/Manuscripts.js index f8ba7ebd..a5297d31 100644 --- a/src/client/components/Manuscripts.js +++ b/src/client/components/Manuscripts.js @@ -8,7 +8,6 @@ import Deck from './Deck'; import Pie from './Pie'; let Manuscripts = props => { - //console.log(props.routeProps) return ( <React.Fragment> <ViewTabs routeProps={props.routeProps} /> @@ -33,14 +32,16 @@ let Manuscripts = props => { } /> <Route - path={'/manuscripts/creation_places'} + path={'/manuscripts/production_places'} render={() => <LeafletMap fetchPlaces={props.fetchPlaces} + fetchingPlaces={props.search.fetchingPlaces} fetchPlace={props.fetchPlace} results={props.search.places} place={props.search.place} mapMode='cluster' + variant='productionPlaces' />} /> <Route @@ -66,7 +67,6 @@ let Manuscripts = props => { }; Manuscripts.propTypes = { - map: PropTypes.object.isRequired, search: PropTypes.object.isRequired, facetFilters: PropTypes.object.isRequired, fetchManuscripts: PropTypes.func.isRequired, diff --git a/src/client/components/ResultTable.js b/src/client/components/ResultTable.js index b2417270..bdb3eee6 100644 --- a/src/client/components/ResultTable.js +++ b/src/client/components/ResultTable.js @@ -247,7 +247,7 @@ class ResultTable extends React.Component { render() { const { classes, rows } = this.props; - console.log(rows) + //console.log(rows) if (this.props.fetchingManuscripts ) { return ( diff --git a/src/client/components/ViewTabs.js b/src/client/components/ViewTabs.js index 98446307..bea2438d 100644 --- a/src/client/components/ViewTabs.js +++ b/src/client/components/ViewTabs.js @@ -37,7 +37,7 @@ class ViewTabs extends React.Component { pathnameToValue = pathname => { let value; switch (pathname) { - case '/manuscripts/creation_places': + case '/manuscripts/production_places': value = 1; break; case '/manuscripts/migrations': @@ -67,8 +67,8 @@ class ViewTabs extends React.Component { indicatorColor="secondary" textColor="secondary" > - <Tab icon={<CalendarViewDayIcon />} label="table" component={Link} to="/manuscripts" /> - <Tab icon={<AddLocationIcon />} label="creation places" component={Link} to="/manuscripts/creation_places" /> + <Tab icon={<CalendarViewDayIcon />} label="manuscripts table" component={Link} to="/manuscripts" /> + <Tab icon={<AddLocationIcon />} label="production places" component={Link} to="/manuscripts/production_places" /> <Tab icon={<RedoIcon />} label="migrations" component={Link} to="/manuscripts/migrations" /> <Tab icon={<PieChartIcon />} label="statistics" component={Link} to="/manuscripts/statistics"/> </Tabs> diff --git a/src/client/containers/MapApp.js b/src/client/containers/MapApp.js index 19121b91..59661f4b 100644 --- a/src/client/containers/MapApp.js +++ b/src/client/containers/MapApp.js @@ -143,7 +143,6 @@ let MapApp = (props) => { const mapStateToProps = (state) => { return { facet: state.facet, - map: state.map, search: state.search, //browser: state.browser, }; @@ -167,7 +166,6 @@ MapApp.propTypes = { // error: PropTypes.object.isRequired, // browser: PropTypes.object.isRequired, facet: PropTypes.object.isRequired, - map: PropTypes.object.isRequired, search: PropTypes.object.isRequired, fetchManuscripts: PropTypes.func.isRequired, fetchPlaces: PropTypes.func.isRequired, diff --git a/src/client/epics/index.js b/src/client/epics/index.js index 1239a781..ec90bbdc 100644 --- a/src/client/epics/index.js +++ b/src/client/epics/index.js @@ -57,7 +57,7 @@ const getPlace = action$ => action$.pipe( ofType(FETCH_PLACE), mergeMap(action => { const searchUrl = apiUrl + 'places'; - const requestUrl = `${searchUrl}/${action.placeId}`; + const requestUrl = `${searchUrl}/${encodeURIComponent(action.placeId)}`; return ajax.getJSON(requestUrl).pipe( map(response => updatePlace({ place: response })) ); diff --git a/src/server/index.js b/src/server/index.js index fc1e305d..0c34f3b3 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,6 +1,7 @@ import express from 'express'; const path = require('path'); import bodyParser from 'body-parser'; + import { getManuscripts, getPlaces, @@ -41,7 +42,6 @@ app.get('/manuscripts', (req, res) => { app.get('/places/:placeId?', (req, res) => { if (req.params.placeId) { return getPlace(req.params.placeId).then(data => { - // console.log(data) res.json(data[0]); }) .catch((err) => { @@ -49,7 +49,7 @@ app.get('/places/:placeId?', (req, res) => { return res.sendStatus(500); }); } else { - const variant = req.query.variant ? req.query.variant : 'creationPlaces'; + const variant = req.query.variant ? req.query.variant : 'productionPlaces'; return getPlaces(variant).then((data) => { // console.log(data); res.json(data); diff --git a/src/server/sparql/Datasets.js b/src/server/sparql/Datasets.js index ab74f0d5..f00b483d 100644 --- a/src/server/sparql/Datasets.js +++ b/src/server/sparql/Datasets.js @@ -125,33 +125,47 @@ module.exports = { } `, - 'creationPlacesQuery': ` + 'productionPlacesQuery': ` PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX wgs84: <http://www.w3.org/2003/01/geo/wgs84_pos#> - PREFIX dc: <http://purl.org/dc/elements/1.1/> + PREFIX dct: <http://purl.org/dc/terms/> PREFIX owl: <http://www.w3.org/2002/07/owl#> PREFIX frbroo: <http://erlangen-crm.org/efrbroo/> PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/> PREFIX mmm-schema: <http://ldf.fi/mmm/schema/> - SELECT ?id ?lat ?long ?prefLabel + SELECT ?id ?lat ?long ?prefLabel ?dataProviderUrl (COUNT(DISTINCT ?manuscript) as ?manuscriptCount) WHERE { - ?id a mmm-schema:Place . - ?id skos:prefLabel ?prefLabel . - ?manuscript ^frbroo:R18_created/crm:P7_took_place_at ?id . - OPTIONAL { - ?id wgs84:lat ?lat ; - wgs84:long ?long . + { + ?id a mmm-schema:Place . + ?id skos:prefLabel ?prefLabel . + OPTIONAL { ?id mmm-schema:data_provider_url ?dataProviderUrl } + ?manuscript ^frbroo:R18_created/crm:P7_took_place_at ?id . + OPTIONAL { + ?id wgs84:lat ?lat ; + wgs84:long ?long . + } + } + UNION + { + ?id a mmm-schema:Place . + ?id skos:prefLabel ?prefLabel . + OPTIONAL { ?id mmm-schema:data_provider_url ?dataProviderUrl } + ?manuscript ^crm:P108_has_produced/crm:P7_took_place_at ?id . + OPTIONAL { + ?id wgs84:lat ?lat ; + wgs84:long ?long . + } } } - GROUP BY ?id ?lat ?long ?prefLabel + GROUP BY ?id ?lat ?long ?prefLabel ?dataProviderUrl `, 'migrationsQuery': ` PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX wgs84: <http://www.w3.org/2003/01/geo/wgs84_pos#> - PREFIX dc: <http://purl.org/dc/elements/1.1/> + PREFIX dct: <http://purl.org/dc/terms/> PREFIX owl: <http://www.w3.org/2002/07/owl#> PREFIX frbroo: <http://erlangen-crm.org/efrbroo/> PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/> @@ -183,24 +197,31 @@ module.exports = { 'placeQuery': ` PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX wgs84: <http://www.w3.org/2003/01/geo/wgs84_pos#> - PREFIX dc: <http://purl.org/dc/elements/1.1/> + PREFIX dct: <http://purl.org/dc/terms/> PREFIX owl: <http://www.w3.org/2002/07/owl#> PREFIX frbroo: <http://erlangen-crm.org/efrbroo/> PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/> PREFIX mmm-schema: <http://ldf.fi/mmm/schema/> - SELECT ?id ?prefLabel ?manuscript__id ?manuscript__entry ?manuscript__manuscriptRecord ?sdbmLink ?source ?parent + SELECT * WHERE { BIND (<PLACE_ID> AS ?id) - BIND(REPLACE(STR(?id), "http://ldf.fi/mmm/place/", "https://sdbm.library.upenn.edu/places/") AS ?sdbmLink) ?id skos:prefLabel ?prefLabel . - ?manuscript__id ^frbroo:R18_created/crm:P7_took_place_at ?id . - ?manuscript__id mmm-schema:entry ?manuscript__entry . - OPTIONAL { ?manuscript__id mmm-schema:manuscript_record ?manuscript__manuscriptRecord } - OPTIONAL { ?id owl:sameAs ?source . } - OPTIONAL { ?id mmm-schema:parent ?parent } - #SERVICE <http://sparql.org/books> { - # ?source dc:title ?title . ?s dc:creator ?a - #} + OPTIONAL { + ?id crm:P89_falls_within ?parent__id . + ?parent__id skos:prefLabel ?parent__prefLabel . + ?parent__id mmm-schema:data_provider_url ?parent__dataProviderUrl . + } + OPTIONAL { ?id mmm-schema:data_provider_url ?dataProviderUrl } + OPTIONAL { ?id owl:sameAs ?sameAs } + { + ?manuscript__id ^frbroo:R18_created/crm:P7_took_place_at ?id . + ?manuscript__id mmm-schema:data_provider_url ?manuscript__dataProviderUrl . + } + UNION + { + ?manuscript__id ^crm:P108_has_produced/crm:P7_took_place_at ?id . + ?manuscript__id mmm-schema:data_provider_url ?manuscript__dataProviderUrl . + } } `, 'facetQuery': ` diff --git a/src/server/sparql/Manuscripts.js b/src/server/sparql/Manuscripts.js index ba1d79c5..c0447f5d 100644 --- a/src/server/sparql/Manuscripts.js +++ b/src/server/sparql/Manuscripts.js @@ -66,7 +66,7 @@ export const getPlaces = variant => { export const getPlace = id => { let { endpoint, placeQuery } = datasetConfig['mmm']; - placeQuery = placeQuery.replace('<PLACE_ID>', `<http://ldf.fi/mmm/place/${id}>`); + placeQuery = placeQuery.replace('<PLACE_ID>', `<${id}>`); return sparqlSearchEngine.doSearch(placeQuery, endpoint, makeObjectList); }; -- GitLab