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