diff --git a/package-lock.json b/package-lock.json index 62ea69832b1da17751932afbb810fba6f52b4a18..bda88f53c0dce00448e835ca3f0821a93c33c661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "webapp", - "version": "2.1.9", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -947,8 +947,15 @@ "@types/node": { "version": "6.14.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.3.tgz", - "integrity": "sha512-V2VrQBCKo4U0rni6tW4AASRDqIO5ZTLDN/Xzrm4mNBr9SGQYZ+7zZJn+hMs89Q8ZCIHzp4aWQPyCpK+rux1YGA==", - "dev": true + "integrity": "sha512-V2VrQBCKo4U0rni6tW4AASRDqIO5ZTLDN/Xzrm4mNBr9SGQYZ+7zZJn+hMs89Q8ZCIHzp4aWQPyCpK+rux1YGA==" + }, + "@types/oboe": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/@types/oboe/-/oboe-2.0.28.tgz", + "integrity": "sha1-OvywzJSfzfd3hh2W6FcfO0hvD6c=", + "requires": { + "@types/node": "*" + } }, "@types/q": { "version": "0.0.32", @@ -5120,6 +5127,11 @@ "statuses": ">= 1.4.0 < 2" } }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, "http-parser-js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", @@ -7444,6 +7456,14 @@ "isobject": "^3.0.1" } }, + "oboe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", + "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", + "requires": { + "http-https": "^1.0.0" + } + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", diff --git a/package.json b/package.json index 2bf0bb7b558083212a34b85c1f51e159ceb08c78..9a31283407287ee72fc16789c2cdf1bd8f999121 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@angular/router": "7.2.4", "@turf/centroid": "^5.1.5", "@turf/helpers": "^6.1.4", + "@types/oboe": "^2.0.28", "angulartics2": "^7.5.2", "bulma": "^0.7.5", "bulma-checkradio": "^2.1.0", @@ -50,6 +51,7 @@ "ngx-cookie-service": "^2.1.0", "ngx-infinite-scroll": "^6.0.1", "node-rsa": "^1.0.3", + "oboe": "^2.1.4", "rxjs": "^6.4.0", "xml2js": "^0.4.19", "zone.js": "^0.8.29" diff --git a/src/app/geosource/services/elasticsearch.service.ts b/src/app/geosource/services/elasticsearch.service.ts index ad91282c6ae4ba8c8cad0b869d2d0b14c9145c99..54f1b5e4b670f9cbee6914bbf5770f7459a073b5 100644 --- a/src/app/geosource/services/elasticsearch.service.ts +++ b/src/app/geosource/services/elasticsearch.service.ts @@ -4,7 +4,7 @@ import { SearchSuggestion, SearchCompletion, Filter, } from '../models'; import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import { Observable, from, Subject } from 'rxjs'; import { map, catchError } from 'rxjs/operators'; import { ErrorService } from '../../core/services'; import { notificationMessages, geosource } from '../../../i18n/traductions'; @@ -12,6 +12,7 @@ import { IPostsESOptions } from '../models/elasticsearch-options.model'; import { Aggregation } from '../models/filter.model'; import { scopesResearch } from '../models/scopes-research'; import { APP_CONFIG } from '../../core/services/app-config.service'; +import * as oboe from 'oboe'; @Injectable() export class ElasticsearchService { @@ -950,6 +951,47 @@ export class ElasticsearchService { ); } + + + getDataStream(uuid: string) { + let counter = 0; + let features = []; + const dataObs = new Observable((obs) => { + oboe(`http://localhost:9200/scrollStream/${uuid}`) + .node('![*]', (data) => { + + // This callback will be called everytime a new object is + // found in the foods array. + features.push(data); + counter += 1; + if (counter === 100) { + counter = 0; + obs.next(features); + features = []; + } + // console.log('Go eat some', data); + }) + .done((things) => { + console.log( + 'there are', things.foods.length, 'things to eat', + 'and', things.nonFoods.length, 'to avoid'); + }); + }); + return dataObs; + + // return this._http.get(`http://localhost:9200/scrollStream/${uuid}`).pipe( + // map((e) => { + // // console.log(e); + // return e; + // }), + // catchError( + // (err) => { + // throw this._errorService.handleError(err, { message: notificationMessages.geosource.getDatasetById }); + // }, + // ), + // ); + } + getDataFromCoordinates(filter, metadataId) { // Set query string options const requestOptions = { diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts index b52ce50430b827a46274b93e85d08b91968c3093..89a4ff5d705381ff0dd5d541ead5e149ddba4f41 100644 --- a/src/app/map/components/map.component.ts +++ b/src/app/map/components/map.component.ts @@ -112,7 +112,7 @@ export class MapComponent implements OnInit, OnDestroy { // Set the basic and default options const options = { container: 'map', - center: [4.85, 45.75], + center: [4.85, 45.75] as mapboxgl.LngLatLike, zoom: 12, maxZoom: 21, }; diff --git a/src/app/map/components/minimap-control.ts b/src/app/map/components/minimap-control.ts index 1b41fbfaaa7d72ee995abd0cf722cae4eda610bf..cacf612587c7f7d09b442eedbc75a16f97540d8f 100644 --- a/src/app/map/components/minimap-control.ts +++ b/src/app/map/components/minimap-control.ts @@ -26,7 +26,7 @@ export class Minimap { width: '300px', height: '170px', style: '', - center: [0, 0], + center: [0, 0] as mapboxgl.LngLatLike, zoom: 6, bounds: 'parent', classes: '', diff --git a/src/app/map/services/map.service.ts b/src/app/map/services/map.service.ts index cad3e12581adfef0b6aa733f1329dad1aae7b52e..dbe21c1ac3514296e79906a1e315acda74b091f6 100644 --- a/src/app/map/services/map.service.ts +++ b/src/app/map/services/map.service.ts @@ -78,6 +78,7 @@ export class MapService { // Subscribe to the error observable and send a notification this._errorSubscription = errorObservable.subscribe((v) => { + console.log(v); this._notificationService.notify( new Notification({ message: notificationMessages.geosource.mapError, @@ -126,27 +127,39 @@ export class MapService { addLayers() { // Check if the metadata has WFS data format. const uriWFS = this.metadata.link.find((e) => { return e.service === linkFormats.wfs; }); - if (uriWFS && this._datasetDetailService.datasetDataNumber < 100000) { - this._currentLayerType = linkFormats.wfs; - this.getWFSFeatures( - this.metadata, - settings.maxDisplayFeatures).subscribe((geojson) => { - this.geojson = geojson; - this.addWFSLayer(); - }); - this._map - .on('zoomend', () => { - this._mapToUpdate.next(); - }) - .on('moveend', () => { - this._mapToUpdate.next(); - }); + // if (uriWFS && this._datasetDetailService.datasetDataNumber < 100000) { + this._currentLayerType = linkFormats.wfs; + // Create the collection feature object + this.geojson = { + type: 'FeatureCollection', + name: uriWFS.name, + features: [], + } as GeoJSON.FeatureCollection; + + // Init the source and layers + this.addWFSLayer(); + + // fetch the data and update the map + this.getWFSFeatures( + this.metadata, + settings.maxDisplayFeatures).subscribe((geojson) => { + const source = this._map.getSource('wfs-polygon') as mapboxgl.GeoJSONSource; + // console.log(this.geojson); + source.setData(this.geojson); + }); + this._map + .on('zoomend', () => { + this._mapToUpdate.next(); + }) + .on('moveend', () => { + this._mapToUpdate.next(); + }); - } else { - this._currentLayerType = linkFormats.wms; - const uriWMS = this.metadata.link.find((e) => { return e.service === linkFormats.wms; }); - this.addWMSLayer(uriWMS); - } + // } else { + // this._currentLayerType = linkFormats.wms; + // const uriWMS = this.metadata.link.find((e) => { return e.service === linkFormats.wms; }); + // this.addWMSLayer(uriWMS); + // } } // Get the features inside the bounds of the map, and update the data @@ -156,9 +169,9 @@ export class MapService { const bounds = this._map.getBounds(); this.getWFSFeatures(this.metadata, settings.maxDisplayFeatures, bounds).subscribe((geojson) => { this.geojson = geojson; - const source1 = this._map.getSource('wfs-clustered-points') as mapboxgl.GeoJSONSource; + // const source1 = this._map.getSource('wfs-clustered-points') as mapboxgl.GeoJSONSource; const source2 = this._map.getSource('wfs-polygon') as mapboxgl.GeoJSONSource; - source1.setData(geojson); + // source1.setData(geojson); source2.setData(geojson); // Notify to the component that the map has been ipdated with new features this._mapUpdated.next(this.totalData); @@ -170,13 +183,13 @@ export class MapService { // - Create the WFS layers from this source // - if the features are 'Point' type, create clustering layers addWFSLayer() { - this._map.addSource('wfs-clustered-points', { - type: 'geojson', - data: this.geojson, - cluster: true, - clusterMaxZoom: 13, // Max zoom to cluster points on - clusterRadius: 45, // Radius of each cluster when clustering points (defaults to 50) - }); + // this._map.addSource('wfs-clustered-points', { + // type: 'geojson', + // data: this.geojson, + // cluster: true, + // clusterMaxZoom: 13, // Max zoom to cluster points on + // clusterRadius: 45, // Radius of each cluster when clustering points (defaults to 50) + // }); this._map.addSource('wfs-polygon', { type: 'geojson', @@ -185,55 +198,55 @@ export class MapService { // Add the layers for 'Point' features (clustered and unclustered layers) // Create steps to display different circle size and colors depending the count - this._map.addLayer({ - id: 'point-features', - type: 'circle', - source: 'wfs-clustered-points', - paint: { - // Use step expressions (https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step) - // with three steps to implement three types of circles: - // * 20px circles when point count is less than 20 - // * 30px circles when point count is between 20 and 50 - // * 40px circles when point count is greater than or equal to 50 - 'circle-color': this.featureColor, - 'circle-radius': [ - 'step', - ['get', 'point_count'], - 20, // 20px - 20, // les than 20 features - 30, // 30px - 50, // until - more 50 features - 40, // 40px - ], - 'circle-stroke-width': [ - 'step', - ['get', 'point_count'], - 4, // 4px - 20, // les than 20 features - 6, // 6px - 50, // until - more 50 features - 11, // 11px - ], - 'circle-stroke-color': this.featureColorHalo, - }, - filter: ['has', 'point_count'], - }); + // this._map.addLayer({ + // id: 'point-features', + // type: 'circle', + // source: 'wfs-clustered-points', + // paint: { + // // Use step expressions (https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step) + // // with three steps to implement three types of circles: + // // * 20px circles when point count is less than 20 + // // * 30px circles when point count is between 20 and 50 + // // * 40px circles when point count is greater than or equal to 50 + // 'circle-color': this.featureColor, + // 'circle-radius': [ + // 'step', + // ['get', 'point_count'], + // 20, // 20px + // 20, // les than 20 features + // 30, // 30px + // 50, // until - more 50 features + // 40, // 40px + // ], + // 'circle-stroke-width': [ + // 'step', + // ['get', 'point_count'], + // 4, // 4px + // 20, // les than 20 features + // 6, // 6px + // 50, // until - more 50 features + // 11, // 11px + // ], + // 'circle-stroke-color': this.featureColorHalo, + // }, + // filter: ['has', 'point_count'], + // }); // Add the cluster count layer (the number inside the circle) - this._map.addLayer({ - id: 'cluster-count', - type: 'symbol', - source: 'wfs-clustered-points', - filter: ['has', 'point_count'], - layout: { - 'text-field': '{point_count_abbreviated}', - 'text-size': 14, - 'text-font': ['Noto Sans Bold'], - }, - paint: { - 'text-color': 'white', - }, - }); + // this._map.addLayer({ + // id: 'cluster-count', + // type: 'symbol', + // source: 'wfs-clustered-points', + // filter: ['has', 'point_count'], + // layout: { + // 'text-field': '{point_count_abbreviated}', + // 'text-size': 14, + // 'text-font': ['Noto Sans Bold'], + // }, + // paint: { + // 'text-color': 'white', + // }, + // }); // For 'Polygon' feature highlighted this._map.addLayer({ @@ -304,80 +317,54 @@ export class MapService { 'line-features-highlight', ); - this._map.loadImage('./assets/img/marker_blue_active.png', (error, image) => { - if (error) throw error; - this._map.addImage('marker-active', image); - - this._map.loadImage('./assets/img/marker_blue_hover.png', (error, image) => { - if (error) throw error; - this._map.addImage('marker-hover', image); - // Add layer + style for the unclustered points highlighted - this._map.addLayer( - { - id: 'unclustered-point-highlighted', - type: 'symbol', - source: 'wfs-clustered-points', - filter: ['!has', 'point_count'], - layout: { - 'icon-image': 'marker-active', - 'icon-size': 0.5, - 'icon-anchor': 'bottom', - 'icon-allow-overlap': true, - visibility: 'none', - }, - }, - ); - this._map.loadImage('./assets/img/marker_blue_normal.png', (error, image) => { - if (error) throw error; - this._map.addImage('marker', image); - // Add layer + style for the unclustered points - this._map.addLayer( - { - id: 'unclustered-point', - type: 'symbol', - source: 'wfs-clustered-points', - filter: ['!has', 'point_count'], - layout: { - 'icon-image': 'marker', - 'icon-size': 0.5, - 'icon-anchor': 'bottom', - 'icon-allow-overlap': true, - }, - }, - 'unclustered-point-highlighted', - ); + // Add layer + style for the unclustered points highlighted + this._map.addLayer( + { + id: 'unclustered-point', + type: 'circle', + source: 'wfs-polygon', + paint: { + 'circle-stroke-width': 1, + 'circle-stroke-opacity': 0.5, + 'circle-stroke-color': '#000', + 'circle-color': this.featureColor, + + }, + }, + ); + + // Add layer + style for the unclustered points + + // this._map.addLayer( + // { + // id: 'unclustered-point-hover', + // type: 'symbol', + // source: 'wfs-clustered-points', + // filter: ['==', '_featureId', ''], + // layout: { + // 'icon-image': 'marker-hover', + // 'icon-size': 0.5, + // 'icon-anchor': 'bottom', + // 'icon-allow-overlap': true, + // visibility: 'none', + // }, + // }, + // ); + - this._map.addLayer( - { - id: 'unclustered-point-hover', - type: 'symbol', - source: 'wfs-clustered-points', - filter: ['==', '_featureId', ''], - layout: { - 'icon-image': 'marker-hover', - 'icon-size': 0.5, - 'icon-anchor': 'bottom', - 'icon-allow-overlap': true, - visibility: 'none', - }, - }, - ); - }); - }); - }); if (!this.eventPopupAdded) { // Change the cursor to a pointer when the mouse is over the unclustered-point layer. this._map.on('mouseenter', 'unclustered-point', (e) => { this._map.getCanvas().style.cursor = 'pointer'; const hoveredFeature = e.features[0].properties._featureId; - this._map.setFilter('unclustered-point-hover', ['==', '_featureId', hoveredFeature]); - this._map.setLayoutProperty('unclustered-point-hover', 'visibility', 'visible'); + // this._map.setFilter('unclustered-point-hover', ['==', '_featureId', hoveredFeature]); + // this._map.setLayoutProperty('unclustered-point-hover', 'visibility', 'visible'); }).on('mouseleave', 'unclustered-point', () => { this._map.getCanvas().style.cursor = ''; - this._map.setFilter('unclustered-point-hover', ['==', '_featureId', '']); - this._map.setLayoutProperty('unclustered-point-hover', 'visibility', 'none'); + // this._map.setFilter('unclustered-point-hover', ['==', '_featureId', '']); + // this._map.setLayoutProperty('unclustered-point-hover', 'visibility', 'none'); }); @@ -534,54 +521,44 @@ export class MapService { coordinates.push([bounds.getSouthEast().lng, bounds.getSouthEast().lat]); } - return this._elasticSearchService.getDataByBbox( - metadata.geonet.uuid, - count, - coordinates).pipe( - map((elasticResponse) => { - const uriWFS = metadata.link.find((e) => { return e.service === linkFormats.wfs; }); - // Create the collection feature object - const featureCollection = { - type: 'FeatureCollection', - name: uriWFS.name, - features: [], - }; - - elasticResponse.hits.hits.forEach((e) => { - if (e._source['data-fr'] !== undefined) { - featureCollection.features.push(e._source['data-fr']); - // Order the feature properties by provider order - const orderedProperties = this._datasetDetailService.dataset.fields.list; - featureCollection.features.forEach((feature) => { - const newDataPropertiesOrder = {}; - orderedProperties.forEach((field) => { - newDataPropertiesOrder[field] = feature.properties[field] ? feature.properties[field] : ''; - }); - feature.properties = newDataPropertiesOrder; - }); - } - }); - - this.totalData = elasticResponse.hits.total; - - const newFeatures = []; - // If the features are 'MultiPoint' type, explode it into multiple 'Point' - featureCollection.features.forEach((feature, index) => { - feature.properties['_featureId'] = index; - if (feature.geometry.type === 'MultiPoint') { - feature.geometry.coordinates.forEach((point) => { - const newFeature = Object.assign(feature); - newFeature.geometry.coordinates = point; - newFeature.geometry.type = 'Point'; - newFeatures.push(newFeature); - }); - } else { - newFeatures.push(feature); - } - }); - featureCollection.features = newFeatures; - return featureCollection as GeoJSON.FeatureCollection; - })); + // return this._elasticSearchService.getDataByBbox( + // metadata.geonet.uuid, + // count, + // coordinates).pipe( + return this._elasticSearchService.getDataStream(this.metadata.geonet.uuid).pipe( + map((data: GeoJSON.Feature[]) => { + this.geojson.features = this.geojson.features.concat(data); + // data.forEach((feature) => { + // this.geojson.features.push(feature); + // }); + // Order the feature properties by provider order + // const orderedProperties = this._datasetDetailService.dataset.fields.list; + // this.geojson.features.forEach((feature) => { + // const newDataPropertiesOrder = {}; + // orderedProperties.forEach((field) => { + // newDataPropertiesOrder[field] = feature.properties[field] ? feature.properties[field] : ''; + // }); + // feature.properties = newDataPropertiesOrder; + // }); + + // const newFeatures = []; + // // If the features are 'MultiPoint' type, explode it into multiple 'Point' + // this.geojson.features.forEach((feature, index) => { + // feature.properties['_featureId'] = index; + // if (feature.geometry.type === 'MultiPoint') { + // feature.geometry.coordinates.forEach((point) => { + // const newFeature = Object.assign(feature); + // newFeature.geometry.coordinates = point; + // newFeature.geometry.type = 'Point'; + // newFeatures.push(newFeature); + // }); + // } else { + // newFeatures.push(feature); + // } + // }); + // this.geojson.features = newFeatures; + return this.geojson as GeoJSON.FeatureCollection; + })); } // Used for WMS layer.