diff --git a/Jenkinsfile b/Jenkinsfile index 68e35ad..bd84b41 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { agent { docker { image 'node:latest' - args '-p 3011:3000' + args '-p 3000:3000' } } environment { @@ -25,4 +25,31 @@ pipeline { } } } -} + post { + always { + script { + properties([[$class: 'GithubProjectProperty', + projectUrlStr: 'https://github.com/clarity-h2020/map-component']]) + } + step([$class: 'GitHubIssueNotifier', + issueAppend: true, + issueReopen: false, + issueLabel: 'CI', + issueTitle: '$JOB_NAME $BUILD_DISPLAY_NAME failed']) + } + failure { + emailext attachLog: true, + to: "pascal@cismet.de", + subject: "Build failed in Jenkins: ${currentBuild.fullDisplayName}", + body: """

FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':

+

Check console output at "${env.JOB_NAME} [${env.BUILD_NUMBER}]"

""" + } + unstable { + emailext attachLog: true, + to: "pascal@cismet.de", + subject: "Jenkins build became unstable: ${currentBuild.fullDisplayName}", + body: """

UNSTABLE: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':

+

Check console output at "${env.JOB_NAME} [${env.BUILD_NUMBER}]"

""" + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index c727b5e..ff18894 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clarity-map-component", - "version": "2.4.6", + "version": "2.5.0", "private": true, "license": "LGPL", "dependencies": { @@ -14,6 +14,7 @@ "leaflet-loading": "latest", "leaflet-providers": "git://github.com/leaflet-extras/leaflet-providers#semver:^1.8.0", "leaflet.nontiledlayer": "^1.0.8", + "leaflet.sync": "^0.2.4", "loglevel": "^1.6.6", "prop-types": "latest", "query-string": "^6.10.1", diff --git a/src/App.js b/src/App.js index 3131f2c..2fc4d85 100644 --- a/src/App.js +++ b/src/App.js @@ -27,32 +27,25 @@ export default class App extends React.Component { - + - - - + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + ); } -} +} \ No newline at end of file diff --git a/src/MapComp.css b/src/MapComp.css deleted file mode 100644 index 7442f45..0000000 --- a/src/MapComp.css +++ /dev/null @@ -1,69 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - animation: App-logo-spin infinite 20s linear; - height: 80px; -} - -.App-header { - background-color: #222; - height: 150px; - padding: 20px; - color: white; -} - -.App-title { - font-size: 1.5em; -} - -.App-intro { - font-size: large; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -.leaflet-container { - width: 100%; - height: 100%; -} - -.leaflet-container { - height: 500px; -} - -.rlglc-active { - height: 500px !important; -} - -.hiddenGroup { - display: none !important; -} - -.hiddenGroupHeader { - background-image: url(../../../../../../modules/custom/map-component/src/img/icon-pigpent.png) !important; - background-repeat: no-repeat; - padding-left: 15px; - background-size: 8px 8px; - background-position: left center; -} - -.rlglc-group { - background-image: url(../../../../../../modules/custom/map-component/src/img/icon-pigpens.png); - background-repeat: no-repeat; - padding-left: 10px; - background-size: 8px 8px; - background-position: left 10px; -} - -.study-area-edit { - margin-left: 15px; -} diff --git a/src/__tests__/CSISHelpers.test.js b/src/__tests__/CSISHelpers.test.js index 604cb7b..05a59e6 100644 --- a/src/__tests__/CSISHelpers.test.js +++ b/src/__tests__/CSISHelpers.test.js @@ -10,8 +10,13 @@ import axios from 'axios'; import log from 'loglevel'; -import { CSISHelpers } from 'csis-helpers-js'; import express from 'express'; + +// required because of https://github.com/clarity-h2020/simple-table-component/issues/4#issuecomment-595114163 +import 'react-app-polyfill/ie9'; +import 'react-app-polyfill/stable'; + +import { CSISHelpers } from 'csis-helpers-js'; import apiResponseStudy from './../__fixtures__/study.json'; import apiResponseDataPackage from './../__fixtures__/dataPackage.json'; import apiResponseResources from './../__fixtures__/resources.json'; @@ -21,6 +26,9 @@ const app = express(); var server; beforeAll(() => { + // required because of https://github.com/clarity-h2020/map-component/issues/43#issuecomment-595621339 + axios.defaults.adapter = require('axios/lib/adapters/http'); + app.get('/jsonapi/group/study', function(req, res) { res.json(apiResponseStudy); }); @@ -41,14 +49,21 @@ beforeAll(() => { res.json(apiResponseResources); }); - server = app.listen(31336, () => log.debug('Example app listening on port 31336!')); + // this is just unbelievable: log.debug() is not printed to console when running the tests in VSCode. WTF?! + server = app.listen(0, () => log.debug(`Example app listening on http://${server.address().address}:${server.address().port}`)); + log.debug(`Example app listening on http://${server.address().address}:${server.address().port}`); }); test('test extract study area from study json', async (done) => { - const response = await axios.get( - 'http://localhost:31336/jsonapi/group/study?filter[id][condition][path]=id&filter[id][condition][operator]=%3D&filter[id][condition][value]=c3609e3e-f80f-482b-9e9f-3a26226a6859' - ); + // does not work if bound d to ipv6 adaress. :-() + // const url = `http://${server.address().address}:${server.address().port}/jsonapi/group/study?filter[id][condition][path]=id&filter[id][condition][operator]=%3D&filter[id][condition][value]=c3609e3e-f80f-482b-9e9f-3a26226a6859`; + const url = `http://localhost:${server.address().port}/jsonapi/group/study?filter[id][condition][path]=id&filter[id][condition][operator]=%3D&filter[id][condition][value]=c3609e3e-f80f-482b-9e9f-3a26226a6859`; + // unbelievable: does not print to console. See https://github.com/facebook/jest/issues/2441 + log.info(url); + const response = await axios.get(url); + // -> error 400 ?! This used to work with a fixed port. since simple things like logging don't seem + // to be possible with jest, and debugging tests in vscode works only 50% of the time, we disable this test. expect.assertions(5); expect(response).toBeDefined(); @@ -158,8 +173,16 @@ test('check for emikat id in study', () => { afterAll(() => { log.debug('afterAll'); - server.close(() => { - //console.log('JSON Server closed'); - //process.exit(0); - }); + if(server) { + server.close(() => { + //console.log('JSON Server closed'); + //process.exit(0); + }); + } else { + // WTF?!!!! + // https://github.com/clarity-h2020/map-component/issues/43#issuecomment-595637179 + + // unfortunely, we'll never see this because of https://github.com/facebook/jest/issues/2441 + log.warn('server undefined'); + } }); diff --git a/src/__tests__/CSISRemoteHelpers.test.js b/src/__tests__/CSISRemoteHelpers.test.js index f300c44..2ce8eab 100644 --- a/src/__tests__/CSISRemoteHelpers.test.js +++ b/src/__tests__/CSISRemoteHelpers.test.js @@ -12,6 +12,11 @@ import axios from 'axios'; import log from 'loglevel'; + +// required because of https://github.com/clarity-h2020/simple-table-component/issues/4#issuecomment-595114163 +import 'react-app-polyfill/ie9'; +import 'react-app-polyfill/stable'; + import { CSISRemoteHelpers, CSISHelpers } from 'csis-helpers-js'; import apiResponseStudy from './../__fixtures__/study.json'; @@ -22,6 +27,9 @@ log.enableAll(); * Set auth headers for live API test */ beforeAll(async (done) => { + // required because of https://github.com/clarity-h2020/map-component/issues/43#issuecomment-595621339 + axios.defaults.adapter = require('axios/lib/adapters/http'); + jest.setTimeout(60000); let cookie = process.env.COOKIE; @@ -114,7 +122,7 @@ describe('Remote API tests with authentication', () => { done(); }); - it.only('[DEV] test get complete Study', async (done) => { + it('[DEV] test get complete Study', async (done) => { const studyGroupNode = await CSISRemoteHelpers.getStudyGroupNodeFromCsis( undefined, 'c3609e3e-f80f-482b-9e9f-3a26226a6859' diff --git a/src/__tests__/EMIKATHelpers.test.js b/src/__tests__/EMIKATHelpers.test.js index a966c52..42f9e87 100644 --- a/src/__tests__/EMIKATHelpers.test.js +++ b/src/__tests__/EMIKATHelpers.test.js @@ -8,7 +8,11 @@ * *************************************************** */ -import { EMIKATHelpers } from 'csis-helpers-js'; + // required because of https://github.com/clarity-h2020/simple-table-component/issues/4#issuecomment-595114163 +import 'react-app-polyfill/ie9'; +import 'react-app-polyfill/stable'; + + import { EMIKATHelpers } from 'csis-helpers-js'; test('[RELEASE] URL without EmikatId', () => { const url = diff --git a/src/components/CharacteriseHazardMap.js b/src/components/CharacteriseHazardMap.js index 77b523b..abcabc0 100644 --- a/src/components/CharacteriseHazardMap.js +++ b/src/components/CharacteriseHazardMap.js @@ -1,8 +1,6 @@ -import React from 'react'; -import MapComponent from './commons/MapComponent'; -import BasicMap from './commons/BasicMap'; +import GenericMap from './GenericMap'; -export default class CharacteriseHazardMap extends BasicMap { +export default class CharacteriseHazardMap extends GenericMap { constructor(props) { // FIXME: Warning: CharacteriseHazardMap(...): When calling super() in `CharacteriseHazardMap`, // make sure to pass up the same props that your component's constructor was passed. @@ -33,20 +31,4 @@ export default class CharacteriseHazardMap extends BasicMap { console.log('characteriseHazard-map -> process URL: ' + url); return super.processUrl(resource, includedArray, url); } - - /** - * Render the map - */ - render() { - return ( - - ); - } } diff --git a/src/components/ExposureMap.js b/src/components/ExposureMap.js index 647a20e..3e0d24b 100644 --- a/src/components/ExposureMap.js +++ b/src/components/ExposureMap.js @@ -1,8 +1,6 @@ -import React from 'react'; -import MapComponent from './commons/MapComponent'; -import BasicMap from './commons/BasicMap'; +import GenericMap from './GenericMap'; -export default class ExposureMap extends BasicMap { +export default class ExposureMap extends GenericMap { constructor(props) { super({ ...props, mapSelectionId: 'eu-gl:exposure-evaluation' }); @@ -15,27 +13,4 @@ export default class ExposureMap extends BasicMap { loading: true }; } - - /** - * Render the map - */ - render() { - if ( - !this.queryParams || - (!this.queryParams.study_uuid && !this.queryParams.resource_uuid && !this.queryParams.datapackage_uuid) - ) { - return super.render(); - } - - return ( - - ); - } } diff --git a/src/components/GenericMap.js b/src/components/GenericMap.js index 1e7b2b2..be7a7ee 100644 --- a/src/components/GenericMap.js +++ b/src/components/GenericMap.js @@ -1,8 +1,11 @@ import React from 'react'; +import log from 'loglevel'; import MapComponent from './commons/MapComponent'; import BasicMap from './commons/BasicMap'; +// yes, order of imports do matter +import 'leaflet.sync'; -export default class Generic extends BasicMap { +export default class GenericMap extends BasicMap { constructor(props) { super(props); @@ -16,6 +19,21 @@ export default class Generic extends BasicMap { }; } + /** + * Synchronise Maps. See https://github.com/jieter/Leaflet.Sync + */ + componentDidUpdate() { + if (this.mapComponentA && this.mapComponentA.leafletMapInstance && this.mapComponentB && this.mapComponentB.leafletMapInstance) { + this.mapComponentA.leafletMapInstance.sync(this.mapComponentB.leafletMapInstance); + // WARNING: flyTo will not work if we apply it to both maps! + this.mapComponentB.leafletMapInstance.sync(this.mapComponentA.leafletMapInstance, + { + noInitialSync: true + }); + log.debug('Map Components synchronised'); + } + } + /** * Logs the url on the console * @@ -35,18 +53,50 @@ export default class Generic extends BasicMap { !this.queryParams || (!this.queryParams.study_uuid && !this.queryParams.resource_uuid && !this.queryParams.datapackage_uuid) ) { + // renders an error message return super.render(); } - return ( - - ); + if (this.props.isSynchronised === true) { + log.info('rendering two sychronised maps'); + return (<> + (this.mapComponentA = mapComponent)} + fly='true' + /> + (this.mapComponentB = mapComponent)} + fly='false' + /> + ); + } else { + log.info('rendering one simple map: ' + this.props.isSynchronised); + return ( + + ); + } + + } } diff --git a/src/components/HazardLocalEffectsMap.js b/src/components/HazardLocalEffectsMap.js index bf2770f..42ea3e0 100644 --- a/src/components/HazardLocalEffectsMap.js +++ b/src/components/HazardLocalEffectsMap.js @@ -1,8 +1,6 @@ -import React from 'react'; -import MapComponent from './commons/MapComponent'; -import BasicMap from './commons/BasicMap'; +import GenericMap from './GenericMap'; -export default class HazardLocalEffectsMap extends BasicMap { +export default class HazardLocalEffectsMap extends GenericMap { constructor(props) { super({ ...props, mapSelectionId: 'eu-gl:hazard-characterization:local-effects' }); @@ -15,27 +13,4 @@ export default class HazardLocalEffectsMap extends BasicMap { loading: true }; } - - /** - * Render the map - */ - render() { - if ( - !this.queryParams || - (!this.queryParams.study_uuid && !this.queryParams.resource_uuid && !this.queryParams.datapackage_uuid) - ) { - return super.render(); - } - - return ( - - ); - } } diff --git a/src/components/VulnerabilityMap.js b/src/components/VulnerabilityMap.js index cd18592..778c65d 100644 --- a/src/components/VulnerabilityMap.js +++ b/src/components/VulnerabilityMap.js @@ -1,11 +1,9 @@ -import React from 'react'; -import MapComponent from './commons/MapComponent'; -import BasicMap from './commons/BasicMap'; +import GenericMap from './GenericMap'; /** * @deprecated this map is not used anymore */ -export default class VulnerabilityMap extends BasicMap { +export default class VulnerabilityMap extends GenericMap { constructor(props) { super({ ...props, mapSelectionId: 'eu-gl:vulnerability-analysis' }); @@ -18,27 +16,4 @@ export default class VulnerabilityMap extends BasicMap { loading: true }; } - - /** - * Render the map - */ - render() { - if ( - !this.queryParams || - (!this.queryParams.study_uuid && !this.queryParams.resource_uuid && !this.queryParams.datapackage_uuid) - ) { - return super.render(); - } - - return ( - - ); - } } diff --git a/src/components/commons/MapComponent.js b/src/components/commons/MapComponent.js index 59ea0ce..657753d 100644 --- a/src/components/commons/MapComponent.js +++ b/src/components/commons/MapComponent.js @@ -1,17 +1,19 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Map, TileLayer, GeoJSON, WMSTileLayer, withLeaflet, LeafletConsumer } from 'react-leaflet'; +import { Map, TileLayer, GeoJSON, WMSTileLayer, withLeaflet, LeafletConsumer, ZoomControl } from 'react-leaflet'; import { ReactLeafletGroupedLayerControl as ReactLeafletGroupedLayerControlForLeafletv1 } from 'react-leaflet-grouped-layer-control'; import turf from 'turf'; import 'leaflet-loading'; import LegendComponent from './LegendComponent.js'; import 'leaflet/dist/leaflet.css'; +import log from 'loglevel'; // See https://github.com/mhasbie/react-leaflet-vectorgrid#usage-with-react-leaflet-v2 const ReactLeafletGroupedLayerControl = withLeaflet(ReactLeafletGroupedLayerControlForLeafletv1); /** - * Render a leaflet map with the given layers + * Render a leaflet map with the given layers. + * This is still not the actual leaflet component but another wrapper. */ export default class MapComponent extends React.Component { constructor(props) { @@ -23,11 +25,13 @@ export default class MapComponent extends React.Component { checkedBaseLayer: props.baseLayers[0].name, overlays: props.overlays, exclusiveGroups: props.exclusiveGroups, - oldOverlay: [] + oldOverlay: [], + mapId: props.mapId ? props.mapId : 'simpleMap' }; + this.baseLayers = props.baseLayers; this.tileLayerUrl = props.baseLayers[0].url; - this.fly = true; + this.fly = props.fly ? true : false; // the leaflet instance, retrieved from the leaflet context // see https://stackoverflow.com/questions/51308835/how-to-use-react-leaflet-context @@ -103,6 +107,7 @@ export default class MapComponent extends React.Component { this.leafletMapInstance.invalidateSize(); if (this.fly && this.props.studyAreaPolygon != null) { + log.info('centering on study area'); this.leafletMapInstance.flyToBounds(this.getBoundsFromArea(this.props.studyAreaPolygon), null); this.fly = false; } @@ -140,7 +145,7 @@ export default class MapComponent extends React.Component { this.hideListener = []; for (var i = 0; i < groupTitles.length; ++i) { const el = groupTitles[i]; - var listener = function() { + var listener = function () { self.showHide(el); }; this.hideListener.push(listener); @@ -173,7 +178,7 @@ export default class MapComponent extends React.Component { if (nextProps.overlays !== this.props.overlays) { this.setState({ overlays: nextProps.overlays }); const thisObj = this; - setTimeout(function() { + setTimeout(function () { thisObj.setState({ overlays: nextProps.overlays }); }, 100); } @@ -186,9 +191,9 @@ export default class MapComponent extends React.Component { */ getBoundsFromArea(area) { const bboxArray = turf.bbox(area); - const corner1 = [ bboxArray[1], bboxArray[0] ]; - const corner2 = [ bboxArray[3], bboxArray[2] ]; - var bounds = [ corner1, corner2 ]; + const corner1 = [bboxArray[1], bboxArray[0]]; + const corner2 = [bboxArray[3], bboxArray[2]]; + var bounds = [corner1, corner2]; return bounds; } @@ -288,16 +293,16 @@ export default class MapComponent extends React.Component { } this.setState({ - overlays: [ ...newOverlays ], + overlays: [...newOverlays], count: this.state.count + 1, oldOverlay: newOverlays }); } onOverlayChange(newOverlays) { - this.overlaysAllTogether = [ ...newOverlays ]; + this.overlaysAllTogether = [...newOverlays]; this.setState({ - overlays: [ ...newOverlays ], + overlays: [...newOverlays], count: this.state.count + 1 }); } @@ -507,13 +512,19 @@ export default class MapComponent extends React.Component { ref={(comp) => this.leafletMapInstance = comp.leafletElement} */} + + + {(context) => { this.leafletMapInstance = context.map; diff --git a/src/index.css b/src/index.css index a7a2fad..73d7fe2 100644 --- a/src/index.css +++ b/src/index.css @@ -9,8 +9,47 @@ body, width: 100%; } -.leaflet-container, -#map { +.simpleMap, #simpleMap { width: 100vw; height: 100vh; } + +.synchronisedMapA, #synchronisedMapA { + width: 50vw; + height: 100vh; + float:left +} + +.synchronisedMapB, #synchronisedMapB { + width: 50vw; + height: 100vh; + float:right +} + +.rlglc-active { + height: 500px !important; +} + +.hiddenGroup { + display: none !important; +} + +.hiddenGroupHeader { + background-image: url(img/icon-pigpent.png) !important; + background-repeat: no-repeat; + padding-left: 15px; + background-size: 8px 8px; + background-position: left center; +} + +.rlglc-group { + background-image: url(img/icon-pigpens.png); + background-repeat: no-repeat; + padding-left: 10px; + background-size: 8px 8px; + background-position: left 10px; +} + +.study-area-edit { + margin-left: 15px; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9fcc7cd..3f3b9c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1826,9 +1826,9 @@ acorn-walk@^6.0.1: integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== acorn@^5.5.3: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== acorn@^6.0.1, acorn@^6.0.4, acorn@^6.1.1, acorn@^6.2.1, acorn@~6.4.0: version "6.4.0" @@ -3459,9 +3459,10 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -"csis-helpers-js@git://github.com/clarity-h2020/csis-helpers-js.git#semver:^0.5.3": - version "0.5.3" - resolved "git://github.com/clarity-h2020/csis-helpers-js.git#0438751806e607ae13efe5b27c2c9bdcc34944cb" +"csis-helpers-js@git://github.com/clarity-h2020/csis-helpers-js.git#semver:^0.5.4": + version "0.5.4" + uid "441edd838ea0425f91e358a5ed01e097853e9e3b" + resolved "git://github.com/clarity-h2020/csis-helpers-js.git#441edd838ea0425f91e358a5ed01e097853e9e3b" dependencies: "@babel/runtime" "~7.7.7" axios "^0.19.0" @@ -9436,10 +9437,10 @@ react-leaflet-draw@^0.19.0: dependencies: lodash-es "^4.17.10" -react-leaflet-grouped-layer-control@latest: - version "0.0.13" - resolved "https://registry.yarnpkg.com/react-leaflet-grouped-layer-control/-/react-leaflet-grouped-layer-control-0.0.13.tgz#cfd0e2a62cfd949996cd81bbb0be450468f6ce45" - integrity sha512-+esm6AJ52uCbKbG5o5Td0olVDcQBOd0dDb0j0iw7mdOe5SOiN6HtWx1Sq2GkIfxWPjuDPUs6UmnzkQYp2Ppoag== +"react-leaflet-grouped-layer-control@git://github.com/clarity-h2020/react-leaflet-grouped-layer-control.git#semver:^0.1.1": + version "0.1.0" + uid f1fb2ce7798aed9886da7d8b9f8b084bfe743d1b + resolved "git://github.com/clarity-h2020/react-leaflet-grouped-layer-control.git#f1fb2ce7798aed9886da7d8b9f8b084bfe743d1b" react-leaflet@^2.6.1: version "2.6.1"