From 6d38eed1c249a8d3624eeb8ab2a3248c5b508d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Tue, 23 Apr 2019 15:19:32 +0200 Subject: [PATCH] Partially use strict types mode, add strictNullChecks --- .eslintrc.yaml | 3 - .jshintrc | 3 +- .travis.yml | 2 +- api/src/Map.js | 60 +++- api/src/Themes.js | 30 +- api/src/constants.js | 17 +- contribs/gmf/examples/datepicker.js | 10 +- contribs/gmf/examples/editfeature.js | 19 +- contribs/gmf/examples/layertree.js | 1 + contribs/gmf/examples/layertreeadd.js | 1 + contribs/gmf/examples/lidarprofile.js | 2 +- contribs/gmf/examples/objecteditinghub.js | 37 ++- contribs/gmf/examples/profile.js | 2 +- contribs/gmf/examples/timeslider.js | 8 +- contribs/gmf/examples/xsdattributes.js | 1 + contribs/gmf/src/authentication/Service.js | 2 +- contribs/gmf/src/authentication/component.js | 17 +- .../src/backgroundlayerselector/component.js | 25 +- contribs/gmf/src/contextualdata/component.js | 53 +++- .../src/controllers/AbstractAPIController.js | 6 +- .../src/controllers/AbstractAppController.js | 10 +- .../controllers/AbstractDesktopController.js | 8 +- contribs/gmf/src/controllers/bootstrap.js | 13 +- .../datasource/ExternalDataSourcesManager.js | 60 ++-- contribs/gmf/src/datasource/Helper.js | 8 +- contribs/gmf/src/datasource/Manager.js | 121 +++++--- contribs/gmf/src/disclaimer/component.js | 26 +- .../gmf/src/drawing/drawFeatureComponent.js | 59 +++- .../gmf/src/drawing/featureStyleComponent.js | 13 +- contribs/gmf/src/editing/Snapping.js | 33 +- .../gmf/src/editing/editFeatureComponent.js | 223 ++++++++++--- .../editing/editFeatureSelectorComponent.js | 18 +- .../src/filters/filterselectorComponent.js | 45 ++- .../src/import/importdatasourceComponent.js | 52 ++-- .../import/wmsCapabilityLayertreeComponent.js | 19 +- .../wmtsCapabilityLayertreeComponent.js | 20 +- .../gmf/src/layertree/SyncLayertreeMap.js | 29 +- contribs/gmf/src/layertree/TreeManager.js | 35 ++- contribs/gmf/src/layertree/component.js | 115 +++++-- .../layertree/datasourceGroupTreeComponent.js | 14 +- .../gmf/src/layertree/timeSliderComponent.js | 51 ++- contribs/gmf/src/lidarprofile/Config.js | 2 +- contribs/gmf/src/lidarprofile/Manager.js | 60 +++- contribs/gmf/src/lidarprofile/Measure.js | 13 +- contribs/gmf/src/lidarprofile/Plot.js | 57 +++- contribs/gmf/src/lidarprofile/Utils.js | 20 +- contribs/gmf/src/lidarprofile/component.js | 6 +- .../gmf/src/lidarprofile/panelComponent.js | 33 +- contribs/gmf/src/map/component.js | 21 +- .../gmf/src/map/mousepositionComponent.js | 29 +- .../gmf/src/mobile/measure/areaComponent.js | 20 +- .../gmf/src/mobile/measure/baseComponent.js | 40 ++- .../gmf/src/mobile/measure/lengthComponent.js | 23 +- .../gmf/src/mobile/measure/pointComponent.js | 69 +++-- .../gmf/src/mobile/navigation/component.js | 44 ++- contribs/gmf/src/objectediting/Manager.js | 5 +- contribs/gmf/src/objectediting/Query.js | 3 + contribs/gmf/src/objectediting/component.js | 123 ++++++-- contribs/gmf/src/objectediting/coordinate.js | 3 +- .../objectediting/getWMSFeatureComponent.js | 26 +- .../gmf/src/objectediting/toolsComponent.js | 32 +- contribs/gmf/src/permalink/Permalink.js | 107 +++++-- contribs/gmf/src/permalink/shareComponent.js | 16 +- contribs/gmf/src/print/component.js | 207 ++++++++----- contribs/gmf/src/profile/component.js | 154 +++++---- contribs/gmf/src/profile/drawLineComponent.js | 18 +- contribs/gmf/src/query/gridComponent.js | 68 ++-- contribs/gmf/src/query/windowComponent.js | 23 +- contribs/gmf/src/raster/component.js | 80 +++-- contribs/gmf/src/search/component.js | 86 ++++-- contribs/gmf/src/theme/Themes.js | 60 ++-- contribs/gmf/src/theme/selectorComponent.js | 10 +- contribs/gmf/src/themes.js | 25 +- .../spec/directives/displayquerygrid.spec.js | 8 +- .../services/authenticationservice.spec.js | 37 ++- contribs/gmf/test/spec/services/share.spec.js | 36 ++- .../spec/services/syncLayertreeMap.spec.js | 10 +- examples/backgroundlayer.js | 28 +- examples/backgroundlayerdropdown.js | 32 +- examples/bboxquery.js | 24 +- examples/datepicker.js | 8 +- examples/displaywindow.js | 6 +- examples/elevationProfile.js | 53 ++-- examples/layertree.js | 12 +- examples/locationsearch.js | 34 +- examples/mapfishprint.js | 20 +- examples/mapquery.js | 24 +- examples/measure.js | 9 +- examples/permalink.js | 37 ++- examples/search.js | 13 +- examples/toolActivate.js | 3 + src/Menu.js | 9 +- src/Popover.js | 3 + src/WFSDescribeFeatureType.js | 31 +- src/datasource/DataSource.js | 6 +- src/datasource/DataSources.js | 14 +- src/datasource/Group.js | 5 +- src/datasource/OGC.js | 29 +- src/datasource/WMSGroup.js | 7 +- src/draw/Controller.js | 43 ++- src/draw/point.js | 7 +- src/draw/rectangle.js | 5 +- src/draw/text.js | 7 +- src/editing/attributesComponent.js | 21 +- src/editing/createfeatureComponent.js | 49 ++- .../createregularpolygonfromclickComponent.js | 58 ++-- src/editing/exportfeaturesComponent.js | 19 +- src/filter/RuleHelper.js | 72 ++--- src/filter/component.js | 75 +++-- src/filter/ruleComponent.js | 198 +++++++++--- src/format/Attribute.js | 4 +- src/format/FeatureHash.js | 202 ++++++------ src/format/WFSAttribute.js | 9 +- src/format/XSDAttribute.js | 41 ++- src/geolocation/desktop.js | 6 +- src/geolocation/mobile.js | 21 +- src/googlestreetview/component.js | 44 +-- src/grid/Config.js | 9 + src/grid/component.js | 38 ++- src/interaction/DrawAzimut.js | 60 ++-- src/interaction/Measure.js | 105 +++++-- src/interaction/MeasureArea.js | 32 +- src/interaction/MeasureAzimut.js | 32 +- src/interaction/MeasureLength.js | 14 +- src/interaction/MeasurePointMobile.js | 12 +- src/interaction/MobileDraw.js | 23 +- src/interaction/ModifyCircle.js | 10 +- src/interaction/ModifyRectangle.js | 11 +- src/interaction/Rotate.js | 31 +- src/interaction/Translate.js | 15 +- src/interaction/common.js | 8 +- src/layertree/Controller.js | 5 +- src/map/BackgroundLayerMgr.js | 17 +- src/map/FeatureOverlay.js | 2 +- src/map/LayerHelper.js | 52 ++-- src/map/scaleselector.js | 6 +- src/measure/area.js | 14 +- src/measure/azimut.js | 25 +- src/measure/length.js | 16 +- src/message/displaywindowComponent.js | 29 +- src/message/modalComponent.js | 29 +- src/message/popoverComponent.js | 26 +- src/misc/AutoProjection.js | 3 +- src/misc/FeatureHelper.js | 124 +++++--- src/misc/File.js | 13 +- src/misc/Time.js | 44 ++- src/misc/btnComponent.js | 24 +- src/misc/datepickerComponent.js | 52 ++-- src/misc/decorate.js | 8 +- src/misc/filereaderComponent.js | 6 +- src/misc/filters.js | 4 +- src/misc/php-date-formatter.js | 1 + src/olcs/Service.js | 24 +- src/olcs/controls3d.js | 63 ++-- src/print/Service.js | 83 +++-- src/print/Utils.js | 22 +- src/print/VectorEncoder.js | 17 +- src/profile/d3Elevation.js | 12 +- src/query/MapQuerent.js | 5 +- src/query/Querent.js | 168 ++++++---- src/routing/NominatimInputComponent.js | 8 +- src/routing/RoutingComponent.js | 51 +-- src/routing/RoutingFeatureComponent.js | 56 +++- src/rule/Geometry.js | 2 +- src/search/createGeoJSONBloodhound.js | 4 +- src/search/createLocationSearchBloodhound.js | 14 +- src/statemanager/Service.js | 292 +++++++++--------- src/statemanager/WfsPermalink.js | 50 ++- test/spec/services/print.spec.js | 56 ++-- tsconfig.json | 2 +- 170 files changed, 3985 insertions(+), 1977 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index ac33066afd5..416ebe65901 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -12,9 +12,6 @@ rules: comma-dangle: 0 import/no-unresolved: 0 valid-jsdoc: 0 - '@openlayers/valid-tsdoc': - - error - - requireReturn: false max-len: - error - code: 110 diff --git a/.jshintrc b/.jshintrc index 2b6f469f0ce..2c68de8a886 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,3 +1,4 @@ { - "esversion": 6 + "esversion": 6, + "-W014": true } diff --git a/.travis.yml b/.travis.yml index 97dd5f24970..b19e8505b4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,7 +67,7 @@ script: - '! (npm run typecheck|sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g"|grep ^src)' - '! (npm run typecheck|grep api)' - '! (npm run typecheck|grep gmf)' -- '! (npm run typecheck|grep test)' +- '! (npm run typecheck|grep test/)' - '! (npm run typecheck|grep examples)' - npm run doc - npm run build-api diff --git a/api/src/Map.js b/api/src/Map.js index 846ed3d1655..d716c30dda8 100644 --- a/api/src/Map.js +++ b/api/src/Map.js @@ -36,6 +36,13 @@ import {getFeaturesFromLayer} from './Querent.js'; import * as themes from './Themes.js'; +/** + * @typedef {Object} MarkerOptions + * @property {[number, number]} [position] + * @property {string} [icon] + */ + + /** * @private * @hidden @@ -51,7 +58,9 @@ class Map { * TODO: more options */ constructor(options) { - + if (!constants.extent) { + throw new Error('Missing extent'); + } /** * @private * @type {View} @@ -95,11 +104,15 @@ class Map { })); } if (options.addMiniMap) { + const resolutions = this.view_.getResolutions(); + if (!resolutions) { + throw new Error('Missing resolutions'); + } this.map_.addControl(new OverviewMap({ collapsed: !options.miniMapExpanded, view: new View({ projection: this.view_.getProjection(), - resolutions: this.view_.getResolutions() + resolutions }) })); } @@ -153,7 +166,7 @@ class Map { const hasDescription = feature.get('description') !== undefined; return hasId && hasTitle && hasDescription; }, - style: () => null + style: () => [] }); this.map_.addInteraction(this.selectInteraction_); @@ -200,21 +213,27 @@ class Map { } /** - * @param {Object} options Options. + * @param {MarkerOptions} options Options. * @property {import("ol/coordinate.js").Coordinate} position * @property {string} [icon] * @property {import("ol/size.js").Size} [size] */ addMarker(options = {}) { + const position = options.position ? options.position : this.view_.getCenter(); + if (!position) { + throw new Error('Missing positon'); + } const marker = new Feature({ - geometry: new Point(options.position ? options.position : this.view_.getCenter()) + geometry: new Point(position) }); if (options.icon) { // FIXME: use size? + const image = new Icon({ + src: options.icon + }); + // @ts-ignore: OL issue marker.setStyle(new Style({ - image: new Icon({ - src: options.icon - }) + image })); } this.vectorSource_.addFeature(marker); @@ -267,7 +286,11 @@ class Map { .then((text) => { const attr = options.attr || ['title', 'description']; const lines = text.split(/\r\n|\r|\n/); - const columns = lines.shift().split('\t'); + const shiftedLines = lines.shift(); + if (!shiftedLines) { + throw new Error('Missing shiftedLines'); + } + const columns = shiftedLines.split('\t'); for (const line of lines) { if (line) { const values = zip(columns, line.split('\t')); @@ -276,10 +299,14 @@ class Map { }); marker.setProperties(filterByKeys(values, attr)); marker.setId(values.id); + // FIXME: handle values.iconSize + // FIXME: handle values.iconOffset + const image = new Icon({ + src: values.icon + }); + // @ts-ignore: OL issue marker.setStyle(new Style({ - image: new Icon({ - src: values.icon - }) + image })); this.vectorSource_.addFeature(marker); } @@ -308,7 +335,14 @@ class Map { feature.getGeometry() ).getCoordinates(); const properties = feature.getProperties(); - const content = this.overlay_.getElement().querySelector('.ol-popup-content'); + const element = this.overlay_.getElement(); + if (!element) { + throw new Error('Missing element'); + } + const content = element.querySelector('.ol-popup-content'); + if (!content) { + throw new Error('Missing content'); + } content.innerHTML = ''; content.innerHTML += `
${properties.title}
`; content.innerHTML += `

${properties.description}

`; diff --git a/api/src/Themes.js b/api/src/Themes.js index 7627a61ee9b..00ce6feff75 100644 --- a/api/src/Themes.js +++ b/api/src/Themes.js @@ -19,6 +19,9 @@ let themesPromise; */ function getThemesPromise() { if (!themesPromise) { + if (!constants.themesUrl) { + throw new Error('Missing constants.themesUrl'); + } themesPromise = fetch(constants.themesUrl).then(response => response.json()); } return themesPromise; @@ -72,6 +75,7 @@ export function getBackgroundLayers() { const layerWMS = /** @type {import('gmf/themes.js').GmfLayerWMS} */ (child); return createWMSLayer(layerWMS, themes.ogcServers[child.ogcServer]); } + throw new Error('Unknow layer type'); })); promises.push( groupPromise.then((layers) => { @@ -123,14 +127,13 @@ export function getOverlayDefs() { /** * @param {Object} config Config * @param {import('gmf/themes.js').GmfOgcServers} ogcServers OGC servers - * @param {import('gmf/themes.js').GmfOgcServer} [opt_ogcServer] OGC server + * @param {import('gmf/themes.js').GmfOgcServer} [opt_ogcServer] OGC server * @returns {void} * @hidden */ export function writeOverlayDefs(config, ogcServers, opt_ogcServer) { const group = /** @type {import('gmf/themes.js').GmfGroup} */(config); - const ogcServer = opt_ogcServer ? opt_ogcServer : - group.ogcServer ? ogcServers[group.ogcServer] : undefined; + const ogcServer = opt_ogcServer ? opt_ogcServer : ogcServers[group.ogcServer]; if (group.children) { for (const childConfig of group.children) { writeOverlayDefs(childConfig, ogcServers, ogcServer); @@ -187,15 +190,17 @@ export function getOverlayLayers(layerNames) { * @hidden */ export function createWMSLayer(config, ogcServer) { + const source = new ImageWMS({ + url: ogcServer.url, + projection: undefined, // should be removed in next OL version + params: { + 'LAYERS': config.layers + }, + serverType: ogcServer.type + }); + // @ts-ignore: OL issue const layer = new ImageLayer({ - source: new ImageWMS({ - url: ogcServer.url, - projection: undefined, // should be removed in next OL version - params: { - 'LAYERS': config.layers - }, - serverType: ogcServer.type - }) + source }); layer.set('title', config.name); return Promise.resolve(layer); @@ -213,6 +218,9 @@ export function createWMTSLayer(config) { layer: config.layer, matrixSet: config.matrixSet }); + if (!options) { + throw new Error('Missing options'); + } const source = new WMTS(options); source.updateDimensions(config.dimensions); const layer = new TileLayer({ diff --git a/api/src/constants.js b/api/src/constants.js index f912f43fad2..04ccac60a33 100644 --- a/api/src/constants.js +++ b/api/src/constants.js @@ -1,14 +1,23 @@ import EPSG21781 from '@geoblocks/proj/src/EPSG_21781.js'; -export default { - themesUrl: undefined, +/** + * @typedef {Object} APIConfig + * @property {?string} themesUrl + * @property {string} projection + * @property {Array} resolutions + * @property {?[number, number, number, number]} extent + * @property {string} backgroundLayer + */ + +export default /** @type {APIConfig} */({ + themesUrl: null, projection: EPSG21781, resolutions: [250, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05], - extent: undefined, + extent: null, /** * The name of the layer to use as background. May be a single value * (WMTS) or a comma-separated list of layer names (WMS). */ backgroundLayer: 'orthophoto', -}; +}); diff --git a/contribs/gmf/examples/datepicker.js b/contribs/gmf/examples/datepicker.js index ff34490dd08..2fe674c97f1 100644 --- a/contribs/gmf/examples/datepicker.js +++ b/contribs/gmf/examples/datepicker.js @@ -41,8 +41,6 @@ function MainController($scope, ngeoWMSTime) { widget: TimePropertyWidgetEnum.DATEPICKER, maxValue: '2013-12-31T00:00:00Z', minValue: '2006-01-01T00:00:00Z', - maxDefValue: null, - minDefValue: null, resolution: TimePropertyResolutionEnum.DAY, mode: TimePropertyModeEnum.RANGE, interval: [0, 1, 0, 0] @@ -55,8 +53,6 @@ function MainController($scope, ngeoWMSTime) { widget: /** @type {TimePropertyWidgetEnum} */ ('datepicker'), maxValue: '2015-12-31T00:00:00Z', minValue: '2014-01-01T00:00:00Z', - maxDefValue: null, - minDefValue: null, resolution: /** @type {TimePropertyResolutionEnum}*/ ('month'), mode: /** @type {TimePropertyModeEnum} */ ('value'), interval: [0, 1, 0, 0] @@ -65,12 +61,12 @@ function MainController($scope, ngeoWMSTime) { /** * @type {string} */ - this.value; + this.value = ''; /** * @type {string} */ - this.rangeValue; + this.rangeValue = ''; this.onDateSelected = function(date) { this.value = this.ngeoWMSTime_.formatWMSTimeParam(this.wmsTimeValueMode, date); @@ -79,11 +75,9 @@ function MainController($scope, ngeoWMSTime) { this.onDateRangeSelected = function(date) { this.rangeValue = this.ngeoWMSTime_.formatWMSTimeParam(this.wmsTimeRangeMode, date); }; - } module.controller('MainController', MainController); - export default module; diff --git a/contribs/gmf/examples/editfeature.js b/contribs/gmf/examples/editfeature.js index fdee4323760..87bff63826a 100644 --- a/contribs/gmf/examples/editfeature.js +++ b/contribs/gmf/examples/editfeature.js @@ -78,6 +78,7 @@ function MainController($scope, gmfEditFeature, gmfUser) { * @type {import("ol/layer/Image.js").default} * @private */ + // @ts-ignore: OL issue this.wmsLayer_ = new olLayerImage({ source: this.wmsSource_ }); @@ -95,7 +96,7 @@ function MainController($scope, gmfEditFeature, gmfUser) { this.layerId_ = 113; /** - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ this.feature = null; @@ -143,6 +144,9 @@ MainController.prototype.handleMapSingleClick_ = function(evt) { const map = this.map; const view = map.getView(); const resolution = view.getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } const buffer = resolution * this.pixelBuffer_; const extent = olExtent.buffer( [coordinate[0], coordinate[1], coordinate[0], coordinate[1]], @@ -186,6 +190,9 @@ MainController.prototype.insertFeature = function() { const map = this.map; const view = map.getView(); const resolution = view.getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } const buffer = resolution * -50; // 50 pixel buffer inside the extent const size = /** @type {!Array.} */ (map.getSize()); const extent = olExtent.buffer( @@ -226,8 +233,9 @@ MainController.prototype.insertFeature = function() { * Update the currently selected feature with a new name. */ MainController.prototype.updateFeature = function() { - - console.assert(this.feature); + if (!this.feature) { + throw new Error('Missing feature'); + } this.pending = true; @@ -248,8 +256,9 @@ MainController.prototype.updateFeature = function() { * Delete currently selected feature. */ MainController.prototype.deleteFeature = function() { - - console.assert(this.feature); + if (!this.feature) { + throw new Error('Missing feature'); + } // (1) Launch request this.editFeature_.deleteFeature( diff --git a/contribs/gmf/examples/layertree.js b/contribs/gmf/examples/layertree.js index afa8d27e7ba..16ffcb247d6 100644 --- a/contribs/gmf/examples/layertree.js +++ b/contribs/gmf/examples/layertree.js @@ -193,6 +193,7 @@ function MainController(gmfTreeManager, gmfThemes, gmfThemeManager, ngeoLocation if (n.id === node.id) { return alreadyAdded = true; } + return false; }); if (!alreadyAdded) { nodes.push(node); diff --git a/contribs/gmf/examples/layertreeadd.js b/contribs/gmf/examples/layertreeadd.js index 7ab6826560d..3fd906dd995 100644 --- a/contribs/gmf/examples/layertreeadd.js +++ b/contribs/gmf/examples/layertreeadd.js @@ -193,6 +193,7 @@ function MainController(gmfTreeManager, gmfThemes, gmfThemeManager, ngeoLocation if (n.id === node.id) { return alreadyAdded = true; } + return false; }); if (!alreadyAdded) { nodes.push(node); diff --git a/contribs/gmf/examples/lidarprofile.js b/contribs/gmf/examples/lidarprofile.js index 1d740e60590..d5d415c1955 100644 --- a/contribs/gmf/examples/lidarprofile.js +++ b/contribs/gmf/examples/lidarprofile.js @@ -32,7 +32,7 @@ module.value('pytreeLidarprofileJsonUrl', 'https://sitn.ne.ch/pytree'); */ function MainController($scope) { /** - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} */ this.profileLine = null; diff --git a/contribs/gmf/examples/objecteditinghub.js b/contribs/gmf/examples/objecteditinghub.js index e2abecbc2a4..c1bc3b50ec6 100644 --- a/contribs/gmf/examples/objecteditinghub.js +++ b/contribs/gmf/examples/objecteditinghub.js @@ -101,16 +101,16 @@ function MainController($http, $q, $scope, gmfThemes, gmfXSDAttributes) { this.selectedUrl = this.urls[0]; /** - * @type {import('gmf/themes.js').GmfOgcServers} ogcServers OGC servers. + * @type {?import('gmf/themes.js').GmfOgcServers} ogcServers OGC servers. * @private */ - this.gmfServers_; + this.gmfServers_ = null; /** - * @type {import('gmf/themes.js').GmfOgcServer} ogcServer OGC server to use. + * @type {?import('gmf/themes.js').GmfOgcServer} ogcServer OGC server to use. * @private */ - this.gmfServer_; + this.gmfServer_ = null; /** * @type {Array} @@ -130,7 +130,7 @@ function MainController($http, $q, $scope, gmfThemes, gmfXSDAttributes) { /** * @type {Array} */ - this.features = null; + this.features = []; /** * @type {?import("ol/Feature.js").default} @@ -184,8 +184,8 @@ function MainController($http, $q, $scope, gmfThemes, gmfXSDAttributes) { let i, ii; // (2) Find OE theme - /** @type {import('gmf/themes.js').GmfTheme} */ - let theme; + /** @type {?import('gmf/themes.js').GmfTheme} */ + let theme = null; for (i = 0, ii = themes.length; i < ii; i++) { if (themes[i].name === this.themeName) { theme = themes[i]; @@ -202,6 +202,9 @@ function MainController($http, $q, $scope, gmfThemes, gmfXSDAttributes) { // (4) Set OGC server, which must support WFS for this example to work console.assert(groupNode.ogcServer); + if (!this.gmfServers_) { + throw new Error('Missing gmfServers'); + } const gmfServer = this.gmfServers_[groupNode.ogcServer]; if (gmfServer && gmfServer.wfsSupport === true && gmfServer.urlWfs) { this.gmfServer_ = gmfServer; @@ -232,6 +235,12 @@ function MainController($http, $q, $scope, gmfThemes, gmfXSDAttributes) { /** */ MainController.prototype.runEditor = function() { + if (!this.selectedGmfLayerNode) { + throw new Error('Missing selectedGmfLayerNode'); + } + if (!this.selectedFeature) { + throw new Error('Missing selectedFeature'); + } const geomType = this.selectedGeomType; const feature = this.selectedFeature; @@ -271,6 +280,9 @@ MainController.prototype.runViewerHosted = function() { * @private */ MainController.prototype.runViewer_ = function(baseUrl) { + if (!this.selectedGmfLayerNode) { + throw new Error('Missing selectedGmfLayerNode'); + } const node = this.selectedGmfLayerNode; const nodeId = node.id; @@ -287,7 +299,7 @@ MainController.prototype.runViewer_ = function(baseUrl) { } const params = {}; - params['wfs_layer'] = nodeName; + params.wfs_layer = nodeName; params[`wfs_${nodeIdAttrFieldName}`] = ids.join(','); const url = MainController.appendParams(baseUrl, params); @@ -321,6 +333,9 @@ MainController.prototype.getFeatures_ = function(gmfLayerNode) { * @private */ MainController.prototype.issueGetFeatures_ = function(gmfLayerNode) { + if (!this.gmfServer_) { + throw new Error('Missing gmfServer'); + } const id = gmfLayerNode.id; @@ -335,6 +350,9 @@ MainController.prototype.issueGetFeatures_ = function(gmfLayerNode) { ); this.http_.get(url).then((response) => { + if (!this.getFeaturesDeferred_) { + throw new Error('Missing getFeaturesDeferred'); + } const features = new olFormatWFS().readFeatures(response.data); this.featuresCache_[id] = features; this.getFeaturesDeferred_.resolve(); @@ -402,6 +420,9 @@ MainController.prototype.issueGetAttributesRequest_ = function( * @param {Array} attributes The attributes */ function(gmfLayerNode, attributes) { + if (!this.getGeometryTypeDeferred_) { + throw new Error('Missing getGeometryTypeDeferred'); + } // Get geom type from attributes and set const geomAttr = getGeometryAttribute(attributes); if (geomAttr && geomAttr.geomType) { diff --git a/contribs/gmf/examples/profile.js b/contribs/gmf/examples/profile.js index f03b27cbd6b..5c7bf3dc3e5 100644 --- a/contribs/gmf/examples/profile.js +++ b/contribs/gmf/examples/profile.js @@ -46,7 +46,7 @@ module.constant('angularLocaleScript', '../build/angular-locale_{{locale}}.js'); */ function MainController($scope, ngeoFeatureOverlayMgr) { /** - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} */ this.profileLine = null; diff --git a/contribs/gmf/examples/timeslider.js b/contribs/gmf/examples/timeslider.js index 87193b9bdcd..af4ffdf3e97 100644 --- a/contribs/gmf/examples/timeslider.js +++ b/contribs/gmf/examples/timeslider.js @@ -38,8 +38,6 @@ function MainController($scope, ngeoWMSTime) { widget: TimePropertyWidgetEnum.SLIDER, maxValue: '2013-12-31T00:00:00Z', minValue: '2006-01-01T00:00:00Z', - maxDefValue: null, - minDefValue: null, resolution: TimePropertyResolutionEnum.DAY, mode: TimePropertyModeEnum.RANGE, interval: [0, 1, 0, 0] @@ -52,8 +50,6 @@ function MainController($scope, ngeoWMSTime) { widget: TimePropertyWidgetEnum.SLIDER, maxValue: '2015-12-31T00:00:00Z', minValue: '2014-01-01T00:00:00Z', - maxDefValue: null, - minDefValue: null, resolution: TimePropertyResolutionEnum.YEAR, mode: TimePropertyModeEnum.VALUE, interval: [0, 0, 1, 0] @@ -62,12 +58,12 @@ function MainController($scope, ngeoWMSTime) { /** * @type {string} */ - this.sliderValue; + this.sliderValue = ''; /** * @type {string} */ - this.sliderRangeValue; + this.sliderRangeValue = ''; this.onDateSelected = function(date) { this.sliderValue = this.ngeoWMSTime_.formatWMSTimeParam(this.wmsTimeValueMode, date); diff --git a/contribs/gmf/examples/xsdattributes.js b/contribs/gmf/examples/xsdattributes.js index 28bd875abc7..865e218a89d 100644 --- a/contribs/gmf/examples/xsdattributes.js +++ b/contribs/gmf/examples/xsdattributes.js @@ -156,6 +156,7 @@ MainController.prototype.getDistinctFlatNodes_ = function(node, nodes) { if (n.id === node.id) { return alreadyAdded = true; } + return false; }); if (!alreadyAdded) { nodes.push(node); diff --git a/contribs/gmf/src/authentication/Service.js b/contribs/gmf/src/authentication/Service.js index 2c7fabc7e35..a7c3ba846e7 100644 --- a/contribs/gmf/src/authentication/Service.js +++ b/contribs/gmf/src/authentication/Service.js @@ -186,7 +186,7 @@ export class AuthenticationService extends olEventsEventTarget { * @return {angular.IPromise} Promise. */ logout() { - const noReload = this.getRolesNames().indexOf(this.noReloadRole_) !== -1; + const noReload = this.noReloadRole_ ? this.getRolesNames().indexOf(this.noReloadRole_) !== -1 : false; const url = `${this.baseUrl_}/${RouteSuffix.LOGOUT}`; return this.$http_.get(url, {withCredentials: true}).then(() => { this.resetUser_(noReload); diff --git a/contribs/gmf/src/authentication/component.js b/contribs/gmf/src/authentication/component.js index 13b37999f65..9ee51a79bac 100644 --- a/contribs/gmf/src/authentication/component.js +++ b/contribs/gmf/src/authentication/component.js @@ -34,7 +34,7 @@ const module = angular.module('gmfAuthentication', [ * @hidden */ function gmfAuthenticationTemplateUrl_(element, attrs) { - const templateUrl = attrs['gmfAuthenticationTemplateurl']; + const templateUrl = attrs.gmfAuthenticationTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/authentication'; } @@ -185,12 +185,12 @@ class AuthenticationController { /** * @type {boolean} */ - this.allowPasswordReset; + this.allowPasswordReset = false; /** * @type {boolean} */ - this.allowPasswordChange; + this.allowPasswordChange = false; /** * @type {PasswordValidator?} @@ -200,7 +200,7 @@ class AuthenticationController { /** * @type {boolean} */ - this.forcePasswordChange; + this.forcePasswordChange = false; /** * @type {?string} @@ -429,11 +429,14 @@ class AuthenticationController { } errors.forEach((error) => { - this.notification_.notify({ + const options = { msg: error, target: container, - type: messageType - }); + }; + if (messageType) { + options.type = messageType; + } + this.notification_.notify(options); }); } diff --git a/contribs/gmf/src/backgroundlayerselector/component.js b/contribs/gmf/src/backgroundlayerselector/component.js index 5cd755755af..b9968016c83 100644 --- a/contribs/gmf/src/backgroundlayerselector/component.js +++ b/contribs/gmf/src/backgroundlayerselector/component.js @@ -20,7 +20,7 @@ module.value('gmfBackgroundlayerselectorTemplateUrl', * @return {string} Template URL. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfBackgroundlayerselectorTemplateurl']; + const templateUrl = $attrs.gmfBackgroundlayerselectorTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/backgroundlayerselector'; } @@ -102,7 +102,7 @@ function Controller($scope, ngeoBackgroundLayerMgr, gmfThemes) { /** * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {!string|undefined} @@ -113,22 +113,22 @@ function Controller($scope, ngeoBackgroundLayerMgr, gmfThemes) { * Function called when a layer was selected by the user. * @type {?Function} */ - this.select; + this.select = null; /** * @type {?import("ol/layer/Base.js").default} */ - this.bgLayer; + this.bgLayer = null; /** * @type {?Array.} */ - this.bgLayers; + this.bgLayers = null; /** - * @type {import("ol/layer/Base.js").default} + * @type {?import("ol/layer/Base.js").default} */ - this.opacityLayer; + this.opacityLayer = null; /** * @type {!import("gmf/theme/Themes.js").ThemesService} @@ -201,7 +201,10 @@ Controller.prototype.handleThemesChange_ = function() { * @returns {number} The background layer opacity. */ Controller.prototype.getSetBgLayerOpacity = function(val) { - if (val !== undefined) { + if (!this.opacityLayer) { + throw new Error('Missing opacityLayer'); + } + if (val !== null) { this.opacityLayer.setOpacity(val); } return this.opacityLayer.getOpacity(); @@ -212,6 +215,9 @@ Controller.prototype.getSetBgLayerOpacity = function(val) { * @param {boolean=} opt_silent Do not notify listeners. */ Controller.prototype.setLayer = function(layer, opt_silent) { + if (!this.map) { + throw new Error('Missing map'); + } this.bgLayer = layer; this.backgroundLayerMgr_.set(this.map, layer); if (!opt_silent && this.select) { @@ -224,6 +230,9 @@ Controller.prototype.setLayer = function(layer, opt_silent) { * @param {import("ol/layer/Base.js").default} layer The opacity background layer. */ Controller.prototype.setOpacityBgLayer = function(layer) { + if (!this.map) { + throw new Error('Missing map'); + } this.backgroundLayerMgr_.setOpacityBgLayer(this.map, layer); }; diff --git a/contribs/gmf/src/contextualdata/component.js b/contribs/gmf/src/contextualdata/component.js index bfce6b60800..d78c22abf2c 100644 --- a/contribs/gmf/src/contextualdata/component.js +++ b/contribs/gmf/src/contextualdata/component.js @@ -62,9 +62,12 @@ function contextualDataComponent() { * @param {angular.IScope} scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} controller Controller. + * @param {angular.IController=} controller Controller. */ link: (scope, element, attrs, controller) => { + if (!controller) { + throw new Error('Missing controller'); + } controller.init(); } }; @@ -88,25 +91,25 @@ module.directive('gmfContextualdata', contextualDataComponent); export function ContextualdataController($compile, $timeout, $scope, gmfRaster, $injector) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {Array} */ - this.projections; + this.projections = []; /** * @type {function(import("ol/coordinate.js").Coordinate, Object):Object} */ - this.callback; + this.callback = (c, o) => ({}); /** - * @type {import("ol/Overlay.js").default} + * @type {?import("ol/Overlay.js").default} * @private */ - this.overlay_; + this.overlay_ = null; /** * @type {angular.ICompileService} @@ -147,6 +150,9 @@ export function ContextualdataController($compile, $timeout, $scope, gmfRaster, */ ContextualdataController.prototype.init = function() { this.preparePopover_(); + if (!this.map) { + throw new Error('Missing map'); + } const mapDiv = this.map.getTargetElement(); console.assert(mapDiv); @@ -161,6 +167,9 @@ ContextualdataController.prototype.init = function() { */ ContextualdataController.prototype.handleMapContextMenu_ = function(event) { this.$scope_.$apply(() => { + if (!this.map) { + throw new Error('Missing map'); + } const pixel = this.map.getEventPixel(event); const coordinate = this.map.getCoordinateFromPixel(pixel); this.setContent_(coordinate); @@ -170,12 +179,21 @@ ContextualdataController.prototype.handleMapContextMenu_ = function(event) { // Use timeout to let the popover content to be rendered before displaying it. this.timeout_(() => { + if (!this.overlay_) { + throw new Error('Missing overlay'); + } this.overlay_.setPosition(coordinate); }); }); }; ContextualdataController.prototype.setContent_ = function(coordinate) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.content_) { + throw new Error('Missing content'); + } const scope = this.$scope_.$new(true); this.$compile_(this.content_)(scope); @@ -207,6 +225,9 @@ ContextualdataController.prototype.setContent_ = function(coordinate) { * @private */ ContextualdataController.prototype.preparePopover_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } const container = document.createElement('DIV'); container.classList.add('popover'); @@ -234,11 +255,25 @@ ContextualdataController.prototype.preparePopover_ = function() { }; ContextualdataController.prototype.showPopover = function() { - this.overlay_.getElement().style.display = 'block'; + if (!this.overlay_) { + throw new Error('Missing overlay'); + } + const element = this.overlay_.getElement(); + if (!element) { + throw new Error('Missing element'); + } + element.style.display = 'block'; }; ContextualdataController.prototype.hidePopover = function() { - this.overlay_.getElement().style.display = 'none'; + if (!this.overlay_) { + throw new Error('Missing overlay'); + } + const element = this.overlay_.getElement(); + if (!element) { + throw new Error('Missing element'); + } + element.style.display = 'none'; }; module.controller('GmfContextualdataController', ContextualdataController); diff --git a/contribs/gmf/src/controllers/AbstractAPIController.js b/contribs/gmf/src/controllers/AbstractAPIController.js index 2edc984c2b9..ced2d31693b 100644 --- a/contribs/gmf/src/controllers/AbstractAPIController.js +++ b/contribs/gmf/src/controllers/AbstractAPIController.js @@ -30,13 +30,17 @@ export class AbstractAPIController extends AbstractAppController { }; Object.assign(viewConfig, config.mapViewConfig || {}); + const scaleline = document.getElementById('scaleline'); + if (!scaleline) { + throw new Error('Missing scaleline'); + } super(config, new olMap({ pixelRatio: config.mapPixelRatio, layers: [], view: new olView(viewConfig), controls: config.mapControls || [ new olControlScaleLine({ - target: document.getElementById('scaleline') + target: scaleline }), new olControlZoom({ zoomInTipLabel: '', diff --git a/contribs/gmf/src/controllers/AbstractAppController.js b/contribs/gmf/src/controllers/AbstractAppController.js index f98cdbd8854..aa839c2c4b6 100644 --- a/contribs/gmf/src/controllers/AbstractAppController.js +++ b/contribs/gmf/src/controllers/AbstractAppController.js @@ -230,6 +230,9 @@ export function AbstractAppController(config, map, $scope, $injector) { if (evt.type !== 'ready') { const themeName = this.permalink_.defaultThemeNameFromFunctionalities(); + if (!themeName) { + throw new Error('Missing themeName'); + } this.gmfThemeManager.updateCurrentTheme(themeName, previousThemeName); } this.setDefaultBackground_(null); @@ -244,7 +247,6 @@ export function AbstractAppController(config, map, $scope, $injector) { * @type {Array} */ this.searchDatasources = [{ - datasetTitle: undefined, labelKey: 'label', groupValues: /** @type {Array} **/ ($injector.get('gmfSearchGroups')), groupActions: /** @type {Array} **/( @@ -432,7 +434,7 @@ export function AbstractAppController(config, map, $scope, $injector) { /** * @type {string} */ - this.lang; + this.lang = ''; /** * Default language @@ -671,7 +673,7 @@ export function AbstractAppController(config, map, $scope, $injector) { /** * @param {Array} layers Layers list. * @param {Array} labels default_basemap list. - * @return {import("ol/layer/Base.js").default} layer or null + * @return {?import("ol/layer/Base.js").default} layer or null * @private * @hidden */ @@ -726,7 +728,7 @@ AbstractAppController.prototype.initLanguage = function() { /** - * @param {import('gmf/themes.js').GmfTheme} theme Theme. + * @param {?import('gmf/themes.js').GmfTheme} theme Theme. * @private */ AbstractAppController.prototype.setDefaultBackground_ = function(theme) { diff --git a/contribs/gmf/src/controllers/AbstractDesktopController.js b/contribs/gmf/src/controllers/AbstractDesktopController.js index 9ec360047ea..cc5a008787b 100644 --- a/contribs/gmf/src/controllers/AbstractDesktopController.js +++ b/contribs/gmf/src/controllers/AbstractDesktopController.js @@ -149,7 +149,7 @@ export class AbstractDesktopController extends AbstractAPIController { }; /** - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} */ this.profileLine = null; @@ -246,7 +246,11 @@ export class AbstractDesktopController extends AbstractAPIController { this.$dataPanel_.resizable('option', 'maxWidth', maxWidth); - if (this.$dataPanel_.width() > maxWidth) { + const width = this.$dataPanel_.width(); + if (!width) { + throw new Error('Missing width'); + } + if (width > maxWidth) { this.$dataPanel_.width(maxWidth); this.map.updateSize(); } diff --git a/contribs/gmf/src/controllers/bootstrap.js b/contribs/gmf/src/controllers/bootstrap.js index 296c2b97bfe..c71bae43b20 100644 --- a/contribs/gmf/src/controllers/bootstrap.js +++ b/contribs/gmf/src/controllers/bootstrap.js @@ -27,9 +27,8 @@ function bootstrap(module) { const interface_ = $('meta[name=interface]')[0].getAttribute('content'); const dynamicUrl_ = $('meta[name=dynamicUrl]')[0].getAttribute('content'); - const dynamicUrl = `${dynamicUrl_}?interface=${interface_}&query=${encodeURIComponent( - document.location.search - )}`; + const search = document.location ? document.location.search || '' : ''; + const dynamicUrl = `${dynamicUrl_}?interface=${interface_}&query=${encodeURIComponent(search)}`; const request = $.ajax(dynamicUrl, { 'dataType': 'json', 'xhrFields': { @@ -40,15 +39,15 @@ function bootstrap(module) { window.alert(`Failed to get the dynamic: ${textStatus}`); }); request.done((dynamic) => { - if (dynamic['doRedirect']) { + if (dynamic.doRedirect) { const small_screen = window.matchMedia ? window.matchMedia('(max-width: 1024px)') : false; if (small_screen && TOUCH) { - window.location.href = dynamic['redirectUrl']; + window.location.href = dynamic.redirectUrl; } } - for (const name in dynamic['constants']) { - module.constant(name, dynamic['constants'][name]); + for (const name in dynamic.constants) { + module.constant(name, dynamic.constants[name]); } angular.bootstrap(document, [`App${interface_}`]); diff --git a/contribs/gmf/src/datasource/ExternalDataSourcesManager.js b/contribs/gmf/src/datasource/ExternalDataSourcesManager.js index 0cff7edb86c..7953560c943 100644 --- a/contribs/gmf/src/datasource/ExternalDataSourcesManager.js +++ b/contribs/gmf/src/datasource/ExternalDataSourcesManager.js @@ -283,10 +283,11 @@ export class ExternalDatSourcesManager { * this service. */ get layerGroup() { - const map = this.map_; - console.assert(map); + if (!this.map_) { + throw new Error('Missing map'); + } return this.ngeoLayerHelper_.getGroupFromMap( - map, + this.map_, EXTERNALLAYERGROUP_NAME ); } @@ -322,9 +323,9 @@ export class ExternalDatSourcesManager { createAndAddDataSourceFromWMSCapability(layer, capabilities, url) { const id = getId(layer); - const service = capabilities['Service']; + const service = capabilities.Service; - url = service['OnlineResource'] || url; + url = service.OnlineResource || url; let dataSource; @@ -332,39 +333,42 @@ export class ExternalDatSourcesManager { if (this.extDataSources_[id]) { dataSource = this.extDataSources_[id]; } else { - const req = capabilities['Capability']['Request']; + const req = capabilities.Capability.Request; // ogcImageType - const formats = req['GetMap']['Format']; + const formats = req.GetMap.Format; const imagePngType = 'image/png'; const ogcImageType = formats.includes(imagePngType) ? imagePngType : formats[0]; // wmsInfoFormat - const infoFormats = req['GetFeatureInfo']['Format']; + const infoFormats = req.GetFeatureInfo.Format; const wmsInfoFormat = infoFormats.includes( WMSInfoFormat.GML ) ? WMSInfoFormat.GML : undefined; // queryable - const queryable = layer['queryable'] === true && + const queryable = layer.queryable === true && wmsInfoFormat !== undefined; // TODO - MaxScaleDenominator // TODO - MinScaleDenominator - dataSource = new ngeoDatasourceOGC({ + const options = { id: id, - name: layer['Title'], + name: layer.Title, ogcImageType: ogcImageType, ogcLayers: [{ - name: layer['Name'], + name: layer.Name, queryable: queryable }], ogcType: Type.WMS, visible: true, - wmsInfoFormat: wmsInfoFormat, wmsUrl: url - }); + }; + if (wmsInfoFormat) { + options.wmsInfoFormat = wmsInfoFormat; + } + dataSource = new ngeoDatasourceOGC(options); // Keep a reference to the external data source in the cache this.extDataSources_[id] = dataSource; @@ -385,7 +389,7 @@ export class ExternalDatSourcesManager { wmsGroup = new ngeoDatasourceWMSGroup({ dataSources: [dataSource], injector: this.injector_, - title: service['Title'], + title: service.Title, url: url }, this.ngeoLayerHelper_); this.addLayer_(wmsGroup.layer); @@ -415,8 +419,8 @@ export class ExternalDatSourcesManager { dataSource = this.extDataSources_[id]; } else { - const name = typeof layer['Title']; - const wmtsLayer = typeof layer['Identifier']; + const name = typeof layer.Title; + const wmtsLayer = typeof layer.Identifier; // TODO - MaxScaleDenominator // TODO - MinScaleDenominator @@ -438,7 +442,7 @@ export class ExternalDatSourcesManager { if (!wmtsGroup) { wmtsGroup = new ngeoDatasourceOGCGroup({ dataSources: [], - title: capabilities['ServiceIdentification']['Title'], + title: capabilities.ServiceIdentification.Title, url: wmtsUrl }); this.addWMTSGroup_(wmtsGroup); @@ -483,6 +487,9 @@ export class ExternalDatSourcesManager { success = false; } else { + if (!this.map_) { + throw new Error('Missing map'); + } // (1) No need to do anything if the file has already been added... if (fileGroup.dataSources.includes(dataSource)) { return; @@ -530,6 +537,9 @@ export class ExternalDatSourcesManager { } else { const ngeoFile = this.ngeoFile_; ngeoFile.read(file).then((content) => { + if (!this.map_) { + throw new Error('Missing map'); + } let features; const readOptions = { featureProjection: this.map_.getView().getProjection() @@ -625,10 +635,11 @@ export class ExternalDatSourcesManager { removeOGCDataSource_(dataSource) { if (dataSource.ogcType === Type.WMS) { // WMS data source - const url = dataSource.wmsUrl; - console.assert(url); + if (!dataSource.wmsUrl) { + throw new Error('Missing dataSource.wmsUrl'); + } - const wmsGroup = this.getWMSGroup(url); + const wmsGroup = this.getWMSGroup(dataSource.wmsUrl); if (wmsGroup && wmsGroup.dataSources.includes(dataSource)) { // Remove from group wmsGroup.removeDataSource(dataSource); @@ -643,10 +654,11 @@ export class ExternalDatSourcesManager { } } else if (dataSource.ogcType === Type.WMTS) { // WMTS data source - const url = dataSource.wmtsUrl; - console.assert(url); + if (!dataSource.wmtsUrl) { + throw new Error('Missing dataSource.wmsUrl'); + } - const wmtsGroup = this.getWMTSGroup(url); + const wmtsGroup = this.getWMTSGroup(dataSource.wmtsUrl); if (wmtsGroup && wmtsGroup.dataSources.includes(dataSource)) { // Remove from group wmtsGroup.removeDataSource(dataSource); diff --git a/contribs/gmf/src/datasource/Helper.js b/contribs/gmf/src/datasource/Helper.js index f67ba12b363..6b10eab33db 100644 --- a/contribs/gmf/src/datasource/Helper.js +++ b/contribs/gmf/src/datasource/Helper.js @@ -50,16 +50,16 @@ export class DatasourceHelper { // === Other properties === /** - * @type {import('ngeo/datasource/DataSource.js').DataSources} + * @type {?import('ngeo/datasource/DataSource.js').DataSources} * @protected */ - this.collection_; + this.collection_ = null; /** - * @type {Object.} + * @type {Object} * @protected */ - this.cache_; + this.cache_ = {}; } /** diff --git a/contribs/gmf/src/datasource/Manager.js b/contribs/gmf/src/datasource/Manager.js index 422e9536012..e63e7adbf85 100644 --- a/contribs/gmf/src/datasource/Manager.js +++ b/contribs/gmf/src/datasource/Manager.js @@ -5,7 +5,7 @@ import gmfLayertreeSyncLayertreeMap, {getLayer} from 'gmf/layertree/SyncLayertre import gmfLayertreeTreeManager from 'gmf/layertree/TreeManager.js'; import gmfThemeThemes, {ThemeNodeType} from 'gmf/theme/Themes.js'; -import {ServerType, WFSOutputFormat, Type} from 'ngeo/datasource/OGC.js'; +import OGC, {ServerType, WFSOutputFormat, Type} from 'ngeo/datasource/OGC.js'; import ngeoDatasourceDataSources from 'ngeo/datasource/DataSources.js'; import ngeoFilterRuleHelper from 'ngeo/filter/RuleHelper.js'; @@ -412,15 +412,15 @@ export class DatasourceManager { * * Once a data source is created, it is added to the data sources cache. * - * @param {import('gmf/themes.js').GmfGroup} firstLevelGroup The first level group node. - * @param {!import('gmf/themes.js').GmfGroup|!import('gmf/themes.js').GmfLayer} node The node, which + * @param {?import('gmf/themes.js').GmfGroup} firstLevelGroup The first level group node. + * @param {import('gmf/themes.js').GmfGroup|import('gmf/themes.js').GmfLayer} node The node, which * may have children or not. - * @param {!import('gmf/themes.js').GmfOgcServers} ogcServers OGC servers. + * @param {import('gmf/themes.js').GmfOgcServers} ogcServers OGC servers. * @private */ createDataSource_(firstLevelGroup, node, ogcServers) { - const groupNode = /** @type {!import('gmf/themes.js').GmfGroup} */ (node); + const groupNode = /** @type {import('gmf/themes.js').GmfGroup} */ (node); const children = groupNode.children; // (1) Group node (node that has children). Loop in the children @@ -537,14 +537,14 @@ export class DatasourceManager { // (7) Dimensions const dimensions = this.dimensions_; - const dimensionsConfig = node.dimensions || firstLevelGroup.dimensions; + const dimensionsConfig = node.dimensions || firstLevelGroup === null ? {} : firstLevelGroup.dimensions; const dimensionsFiltersConfig = gmfLayer.dimensionsFilters; // (8) Time values (lower or lower/upper) let timeLowerValue; let timeUpperValue; if (timeProperty) { - const timeValues = this.ngeoWMSTime_.getOptions(timeProperty)['values']; + const timeValues = this.ngeoWMSTime_.getOptions(timeProperty).values; if (Array.isArray(timeValues)) { timeLowerValue = timeValues[0]; timeUpperValue = timeValues[1]; @@ -560,39 +560,76 @@ export class DatasourceManager { const timeAttributeName = meta.timeAttribute; const visible = meta.isChecked === true; - // Create the data source and add it to the cache - this.dataSourcesCache_[id] = new gmfDatasourceOGC({ + const options = { copyable, - dimensions, dimensionsConfig, dimensionsFiltersConfig, gmfLayer, id, identifierAttribute, - maxResolution, - minResolution, name, - ogcImageType, - ogcLayers, - ogcServerType, - wfsFeatureNS, ogcType, snappable, - snappingTolerance, - snappingToEdges, - snappingToVertice, timeAttributeName, - timeLowerValue, - timeProperty, - timeUpperValue, visible, wfsOutputFormat, - wfsUrl, - wmsIsSingleTile, - wmsUrl, - wmtsLayer, - wmtsUrl - }); + }; + if (dimensions) { + options.dimensions = dimensions; + } + if (maxResolution) { + options.maxResolution = maxResolution; + } + if (minResolution) { + options.minResolution = minResolution; + } + if (ogcImageType) { + options.ogcImageType = ogcImageType; + } + if (ogcLayers) { + options.ogcLayers = ogcLayers; + } + if (ogcServerType) { + options.ogcServerType = ogcServerType; + } + if (wfsFeatureNS) { + options.wfsFeatureNS = wfsFeatureNS; + } + if (snappingTolerance) { + options.snappingTolerance = snappingTolerance; + } + if (snappingToEdges) { + options.snappingToEdges = snappingToEdges; + } + if (snappingToVertice) { + options.snappingToVertice = snappingToVertice; + } + if (timeLowerValue) { + options.timeLowerValue = timeLowerValue; + } + if (timeProperty) { + options.timeProperty = timeProperty; + } + if (timeUpperValue) { + options.timeUpperValue = timeUpperValue; + } + if (wfsUrl) { + options.wfsUrl = wfsUrl; + } + if (wmsIsSingleTile) { + options.wmsIsSingleTile = wmsIsSingleTile; + } + if (wmsUrl) { + options.wmsUrl = wmsUrl; + } + if (wmtsLayer) { + options.wmtsLayer = wmtsLayer; + } + if (wmtsUrl) { + options.wmtsUrl = wmtsUrl; + } + // Create the data source and add it to the cache + this.dataSourcesCache_[id] = new gmfDatasourceOGC(options); } /** @@ -672,7 +709,9 @@ export class DatasourceManager { // (1) Remove data source const dataSource = item.treeCtrl.getDataSource(); - console.assert(dataSource, 'DataSource should be set'); + if (!dataSource) { + throw new Error('DataSource should be set'); + } this.dataSources_.remove(dataSource); // (2) Remove item and clear event listeners @@ -715,7 +754,9 @@ export class DatasourceManager { */ handleTreeCtrlStateChange_(treeCtrl, newVal) { const treeDataSource = treeCtrl.getDataSource(); - console.assert(treeDataSource, 'DataSource should be set'); + if (!treeDataSource) { + throw new Error('DataSource should be set'); + } const visible = newVal === 'on'; treeDataSource.visible = visible; @@ -775,14 +816,16 @@ export class DatasourceManager { layer instanceof olLayerTile ); - const source = /** @type {olLayerImage|olLayerTile} */ (layer).getSource(); - if (!(source instanceof olSourceImageWMS || - source instanceof olSourceTileWMS)) { + if (!(layer instanceof olLayerImage || layer instanceof olLayerTile)) { + return; + } + const source = layer.getSource(); + if (!(source instanceof olSourceImageWMS || source instanceof olSourceTileWMS)) { return; } const params = source.getParams(); - const layersParam = params['LAYERS']; + const layersParam = params.LAYERS; const layersList = layersParam.split(','); console.assert(layersList.length >= 1); @@ -810,6 +853,9 @@ export class DatasourceManager { const treeCtrl = item.treeCtrl; const projCode = treeCtrl.map.getView().getProjection().getCode(); + if (!(dataSource instanceof OGC)) { + throw new Error('Wrong datasource'); + } const filterString = dataSource.visible ? this.ngeoRuleHelper_.createFilterString({ dataSource: dataSource, @@ -883,12 +929,15 @@ export class DatasourceManager { let timeParam; const range = dataSource.timeRangeValue; if (range) { + if (!timeProperty) { + throw new Error('Missing timeProperty'); + } timeParam = this.ngeoWMSTime_.formatWMSTimeParam(timeProperty, range); } // No need to update the TIME param if already the same value; const params = wmsSource.getParams(); - const currentTimeParam = params['TIME']; + const currentTimeParam = params.TIME; if (currentTimeParam === timeParam) { return; } @@ -897,7 +946,7 @@ export class DatasourceManager { // gets reset. this.ngeoLayerHelper_.updateWMSLayerState( wmsLayer, - wmsSource.getParams()['LAYERS'], + wmsSource.getParams().LAYERS, timeParam ); } diff --git a/contribs/gmf/src/disclaimer/component.js b/contribs/gmf/src/disclaimer/component.js index 23f1110d0b8..a40e6c5f18a 100644 --- a/contribs/gmf/src/disclaimer/component.js +++ b/contribs/gmf/src/disclaimer/component.js @@ -61,7 +61,7 @@ function Controller( /** * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {boolean|undefined} @@ -149,6 +149,9 @@ function Controller( * Initialise the controller. */ Controller.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } this.layerVisibility = this.layerVisibility !== undefined ? this.layerVisibility : true; this.dataLayerGroup_ = this.ngeoLayerHelper_.getGroupFromMap(this.map, DATALAYERGROUP_NAME); @@ -279,6 +282,9 @@ Controller.prototype.unregisterLayer_ = function(layer) { Controller.prototype.$onDestroy = function() { + if (!this.dataLayerGroup_) { + throw new Error('Missing dataLayerGroup'); + } this.unregisterLayer_(this.dataLayerGroup_); }; @@ -296,12 +302,15 @@ Controller.prototype.showDisclaimerMessage_ = function(msg) { this.msg = `${this.sce_.trustAsHtml(this.msgs_.join('
'))}`; this.visibility = true; } else { - this.disclaimer_.alert({ - popup: this.popup, + const options = { msg: msg, target: this.element_, type: MessageType.WARNING - }); + }; + if (this.popup) { + options.popup = this.popup; + } + this.disclaimer_.alert(options); } }; @@ -317,12 +326,15 @@ Controller.prototype.closeDisclaimerMessage_ = function(msg) { this.msgs_.length = 0; this.msg = ''; } else { - this.disclaimer_.close({ - popup: this.popup, + const options = { msg: msg, target: this.element_, type: MessageType.WARNING - }); + }; + if (this.popup) { + options.popup = this.popup; + } + this.disclaimer_.close(options); } }; diff --git a/contribs/gmf/src/drawing/drawFeatureComponent.js b/contribs/gmf/src/drawing/drawFeatureComponent.js index 716b68f6e49..8e1eb371ae6 100644 --- a/contribs/gmf/src/drawing/drawFeatureComponent.js +++ b/contribs/gmf/src/drawing/drawFeatureComponent.js @@ -105,18 +105,14 @@ module.directive('gmfDrawfeature', drawinfDrawFeatureComponent); function Controller($scope, $timeout, gettextCatalog, ngeoFeatureHelper, ngeoFeatures, ngeoToolActivateMgr) { /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {boolean} */ - this.active; - - if (this.active === undefined) { - this.active = false; - } + this.active = false; /** * @type {boolean} @@ -297,6 +293,9 @@ function Controller($scope, $timeout, gettextCatalog, ngeoFeatureHelper, ngeoFea $scope.$watch( () => this.selectedFeature, (newFeature, previousFeature) => { + if (!this.map) { + throw new Error('Missing map'); + } this.selectedFeatures.clear(); if (previousFeature) { this.featureHelper_.setStyle(previousFeature); @@ -340,6 +339,12 @@ function Controller($scope, $timeout, gettextCatalog, ngeoFeatureHelper, ngeoFea */ Controller.prototype.closeMenu_ = function() { if (this.menu_) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.menuListenerKey_) { + throw new Error('Missing menuListenerKey'); + } this.map.removeOverlay(this.menu_); this.menu_ = null; olEvents.unlistenByKey(this.menuListenerKey_); @@ -365,6 +370,9 @@ Controller.prototype.initializeInteractions_ = function() { */ Controller.prototype.registerInteractions_ = function() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(interaction); }); }; @@ -376,6 +384,9 @@ Controller.prototype.registerInteractions_ = function() { */ Controller.prototype.unregisterInteractions_ = function() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.removeInteraction(interaction); }); }; @@ -516,8 +527,10 @@ Controller.prototype.handleFeaturesRemove_ = function(evt) { * @param {boolean} active Whether the map select is active or not. * @private */ -Controller.prototype.handleMapSelectActiveChange_ = function( - active) { +Controller.prototype.handleMapSelectActiveChange_ = function(active) { + if (!this.map) { + throw new Error('Missing map'); + } const mapDiv = this.map.getViewport(); console.assert(mapDiv); @@ -544,9 +557,12 @@ Controller.prototype.handleMapSelectActiveChange_ = function( */ Controller.prototype.handleMapClick_ = function(evt) { if (evt instanceof MapBrowserEvent) { + if (!this.map) { + throw new Error('Missing map'); + } const pixel = evt.pixel; - let feature = /** @type {import('ol/Feature.js').default|undefined} */ (this.map.forEachFeatureAtPixel( + let feature = this.map.forEachFeatureAtPixel( pixel, (feature) => { let ret = null; @@ -561,7 +577,7 @@ Controller.prototype.handleMapClick_ = function(evt) { hitTolerance: 5, layerFilter: undefined } - )); + ); feature = feature ? feature : null; @@ -593,6 +609,9 @@ Controller.prototype.handleMapTouchStart_ = function(evt) { * @private */ Controller.prototype.handleMapTouchEnd_ = function(evt) { + if (!this.longPressTimeout_) { + throw new Error('Missing longPressTimeout'); + } clearTimeout(this.longPressTimeout_); }; @@ -603,11 +622,14 @@ Controller.prototype.handleMapTouchEnd_ = function(evt) { */ Controller.prototype.handleMapContextMenu_ = function(evt) { if (evt instanceof Event) { + if (!this.map) { + throw new Error('Missing map'); + } const gettextCatalog = this.gettextCatalog_; const pixel = this.map.getEventPixel(evt); const coordinate = this.map.getCoordinateFromPixel(pixel); - let feature = /** @type {import('ol/Feature.js').default|undefined} */ (this.map.forEachFeatureAtPixel( + let feature = this.map.forEachFeatureAtPixel( pixel, (feature) => { if (feature instanceof Feature) { @@ -622,7 +644,7 @@ Controller.prototype.handleMapContextMenu_ = function(evt) { hitTolerance: 7, layerFilter: undefined } - )); + ); feature = feature ? feature : null; @@ -632,9 +654,11 @@ Controller.prototype.handleMapContextMenu_ = function(evt) { this.closeMenu_(); let actions = []; - - const vertexInfo = this.featureHelper_.getVertexInfoAtCoordinate( - feature, coordinate, this.map.getView().getResolution()); + const resolution = this.map.getView().getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } + const vertexInfo = this.featureHelper_.getVertexInfoAtCoordinate(feature, coordinate, resolution); if (!vertexInfo) { const type = this.featureHelper_.getType(feature); if (type == ngeoGeometryType.CIRCLE || @@ -696,6 +720,9 @@ Controller.prototype.handleMapContextMenu_ = function(evt) { */ Controller.prototype.handleMenuActionClick_ = function(vertexInfo, evt) { const action = /** @type{import('ngeo/filter/ruleComponent.js').MenuEvent} */(evt).detail.action; + if (!this.selectedFeature) { + throw new Error('Missing selectedFeature'); + } switch (action) { case 'delete': diff --git a/contribs/gmf/src/drawing/featureStyleComponent.js b/contribs/gmf/src/drawing/featureStyleComponent.js index a2c9dc69a23..27875bd7dc0 100644 --- a/contribs/gmf/src/drawing/featureStyleComponent.js +++ b/contribs/gmf/src/drawing/featureStyleComponent.js @@ -74,7 +74,7 @@ function Controller($scope, ngeoFeatureHelper) { /** * @type {?import("ol/Feature.js").default} */ - this.feature; + this.feature = null; /** * @type {!angular.IScope} @@ -168,7 +168,9 @@ Controller.prototype.handleFeatureSet_ = function(newFeature, previousFeature) { }); const geometry = newFeature.getGeometry(); - console.assert(geometry, 'Geometry should be thruthy'); + if (!geometry) { + throw new Error('Missing geometry'); + } keys.push( olEvents.listen( @@ -271,6 +273,9 @@ Controller.prototype.getSetStroke = function(value) { * @private */ Controller.prototype.getSetProperty_ = function(key, value) { + if (!this.feature) { + throw new Error('Missing feature'); + } if (value !== undefined) { this.feature.set(key, value); } @@ -296,7 +301,9 @@ Controller.prototype.handleFeatureChange_ = function() { * @private */ Controller.prototype.handleGeometryChange_ = function() { - console.assert(this.feature); + if (!this.feature) { + throw new Error('Missing feature'); + } this.measure = this.featureHelper_.getMeasure(this.feature); const showMeasure = this.featureHelper_.getShowMeasureProperty(this.feature); diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 802c59cb060..9505b079632 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -124,14 +124,18 @@ export function EditingSnappingService($http, $q, $rootScope, $timeout, gmfTheme * */ EditingSnappingService.prototype.ensureSnapInteractionsOnTop = function() { + if (!this.map_) { + throw new Error('Missing map'); + } const map = this.map_; - console.assert(map); let item; for (const uid in this.cache_) { item = this.cache_[+uid]; if (item.active) { - console.assert(item.interaction); + if (!item.interaction) { + throw new Error('Missing item.interaction'); + } map.removeInteraction(item.interaction); map.addInteraction(item.interaction); } @@ -148,6 +152,9 @@ EditingSnappingService.prototype.setMap = function(map) { const keys = this.listenerKeys_; if (this.map_) { + if (!this.treeCtrlsUnregister_) { + throw new Error('Missing treeCtrlsUnregister'); + } this.treeCtrlsUnregister_(); this.unregisterAllTreeCtrl_(); keys.forEach(olEvents.unlistenByKey); @@ -166,6 +173,9 @@ EditingSnappingService.prototype.setMap = function(map) { // leaf nodes are created and they are the ones we're looking for here. this.timeout_(() => { if (value) { + if (!this.gmfTreeManager_.rootCtrl) { + throw new Error('Missing gmfTreeManager_.rootCtrl'); + } this.unregisterAllTreeCtrl_(); this.gmfTreeManager_.rootCtrl.traverseDepthFirst(this.registerTreeCtrl_.bind(this)); } @@ -226,6 +236,9 @@ EditingSnappingService.prototype.registerTreeCtrl_ = function(treeCtrl) { ); const ogcServer = this.getOGCServer_(treeCtrl); + if (!ogcServer) { + throw new Error('Missing ogcServer'); + } this.cache_[uid] = { active: false, featureNS: ogcServer.wfsFeatureNS, @@ -292,6 +305,9 @@ EditingSnappingService.prototype.getOGCServer_ = function(treeCtrl) { if (!ogcServerName) { return null; } + if (!this.ogcServers_) { + throw new Error('Missing ogcServers'); + } return this.ogcServers_[ogcServerName]; }; @@ -386,6 +402,9 @@ EditingSnappingService.prototype.handleTreeCtrlStateChange_ = function(treeCtrl, * @private */ EditingSnappingService.prototype.activateItem_ = function(item) { + if (!this.map_) { + throw new Error('Missing map'); + } // No need to do anything if item is already active if (item.active) { @@ -420,6 +439,9 @@ EditingSnappingService.prototype.activateItem_ = function(item) { * @private */ EditingSnappingService.prototype.deactivateItem_ = function(item) { + if (!this.map_) { + throw new Error('Missing map'); + } // No need to do anything if item is already inactive if (!item.active) { @@ -427,9 +449,11 @@ EditingSnappingService.prototype.deactivateItem_ = function(item) { } const map = this.map_; - console.assert(map); const interaction = item.interaction; + if (!interaction) { + throw new Error('Missing interaction'); + } map.removeInteraction(interaction); item.interaction = null; @@ -477,6 +501,9 @@ EditingSnappingService.prototype.refresh = function() { * @private */ EditingSnappingService.prototype.loadItemFeatures_ = function(item) { + if (!this.map_) { + throw new Error('Missing map'); + } // If a previous request is still running, cancel it. if (item.requestDeferred) { diff --git a/contribs/gmf/src/editing/editFeatureComponent.js b/contribs/gmf/src/editing/editFeatureComponent.js index a0dc09fe154..79efc494644 100644 --- a/contribs/gmf/src/editing/editFeatureComponent.js +++ b/contribs/gmf/src/editing/editFeatureComponent.js @@ -49,6 +49,7 @@ import olStyleStyle from 'ol/style/Style.js'; import olStyleText from 'ol/style/Text.js'; import MapBrowserEvent from 'ol/MapBrowserEvent.js'; import {CollectionEvent} from 'ol/Collection.js'; +import VectorSource from 'ol/source/Vector.js'; /** * The different possible values of the `state` inner property. @@ -211,34 +212,34 @@ function Controller($element, $q, $scope, $timeout, * properties change, which includes the geometry. * @type {boolean} */ - this.dirty; + this.dirty = false; /** - * @type {import("ngeo/layertree/Controller.js").LayertreeController} + * @type {?import("ngeo/layertree/Controller.js").LayertreeController} */ - this.editableTreeCtrl; + this.editableTreeCtrl = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * The state property shared with the `gmf-editfeatureselector` directive. * For more info, see in that directive. * @type {string} */ - this.state; + this.state = ''; /** * @type {number} */ - this.tolerance; + this.tolerance = 0; /** - * @type {import("ol/layer/Vector.js").default} + * @type {?import("ol/layer/Vector.js").default} */ - this.vectorLayer; + this.vectorLayer = null; /** * @type {boolean} @@ -324,21 +325,21 @@ function Controller($element, $q, $scope, $timeout, // === Private properties === /** - * @type {import('gmf/themes.js').GmfLayer} + * @type {?import('gmf/themes.js').GmfLayer} * @private */ - this.editableNode_; + this.editableNode_ = null; /** - * @type {import("ol/layer/Image.js").default|import("ol/layer/Tile.js").default} + * @type {?import("ol/layer/Image.js").default|import("ol/layer/Tile.js").default} * @private */ - this.editableWMSLayer_; + this.editableWMSLayer_ = null; /** * A deferred object resolved after the confirm modal "continue w/o saving" or * "save" buttons are clicked. - * @type {angular.IDeferred|null} + * @type {?angular.IDeferred} * @private */ this.confirmDeferred_ = null; @@ -398,9 +399,9 @@ function Controller($element, $q, $scope, $timeout, this.featureId = undefined; /** - * @type {import("ol/Collection.js").default} + * @type {?import("ol/Collection.js").default} */ - this.features; + this.features = null; /** * @type {import("ol/Collection.js").default} @@ -409,15 +410,15 @@ function Controller($element, $q, $scope, $timeout, this.interactions_ = new olCollection(); /** - * @type {import("ol/interaction/Modify.js").default} + * @type {?import("ol/interaction/Modify.js").default} * @private */ - this.modify_; + this.modify_ = null; /** - * @type {import("ngeo/misc/ToolActivate.js").default} + * @type {?import("ngeo/misc/ToolActivate.js").default} */ - this.modifyToolActivate; + this.modifyToolActivate = null; /** * @type {import("ngeo/Menu.js").default} @@ -448,42 +449,42 @@ function Controller($element, $q, $scope, $timeout, }); /** - * @type {import("ngeo/interaction/Translate.js").default} + * @type {?import("ngeo/interaction/Translate.js").default} * @private */ - this.translate_; + this.translate_ = null; /** - * @type {import("ngeo/interaction/Rotate.js").default} + * @type {?import("ngeo/interaction/Rotate.js").default} * @private */ - this.rotate_; + this.rotate_ = null; /** - * @type {!import("ngeo/misc/ToolActivate.js").default} + * @type {?import("ngeo/misc/ToolActivate.js").default} */ - this.rotateToolActivate; + this.rotateToolActivate = null; /** - * @type {!import("ngeo/misc/ToolActivate.js").default} + * @type {?import("ngeo/misc/ToolActivate.js").default} */ - this.translateToolActivate; + this.translateToolActivate = null; /** - * @type {!Array.} + * @type {Array} * @private */ this.listenerKeys_ = []; /** - * @type {?Array.} + * @type {?Array} */ this.attributes = null; /** - * @type {string|undefined} + * @type {?string} */ - this.geomType; + this.geomType = null; /** * @type {boolean} @@ -512,6 +513,9 @@ function Controller($element, $q, $scope, $timeout, * Called on initialization of the controller. */ Controller.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } const lang = this.gettextCatalog_.getCurrentLanguage(); // @ts-ignore: $.datetimepicker is available, as it is imported @@ -521,9 +525,17 @@ Controller.prototype.$onInit = function() { // (1) Set default values and other properties this.dirty = this.dirty === true; - this.editableNode_ = /** @type {import('gmf/themes.js').GmfLayer} */ ( - this.editableTreeCtrl.node); - const source = /** @type {import('ol/source/Vector.js').default} */(this.vectorLayer.getSource()); + if (!this.editableTreeCtrl) { + throw new Error('Missing editableTreeCtrl'); + } + this.editableNode_ = /** @type {import('gmf/themes.js').GmfLayer} */ (this.editableTreeCtrl.node); + if (!this.vectorLayer) { + throw new Error('Missing vectorLayer'); + } + const source = this.vectorLayer.getSource(); + if (!(source instanceof VectorSource)) { + throw new Error('Wrong source'); + } this.features = source.getFeaturesCollection(); this.tolerance = this.tolerance !== undefined ? this.tolerance : 10; @@ -664,7 +676,15 @@ Controller.prototype.$onInit = function() { * Save the currently selected feature modifications. */ Controller.prototype.save = function() { - console.assert(this.attributes); + if (!this.attributes) { + throw new Error('Missing attributes'); + } + if (!this.feature) { + throw new Error('Missing feature'); + } + if (!this.editableNode_) { + throw new Error('Missing editableNode'); + } const feature = this.feature.clone(); feature.setId(this.feature.getId()); @@ -716,8 +736,8 @@ Controller.prototype.save = function() { }) .catch((response) => { this.showServerError = true; - this.serverErrorType = `error type : ${response.data['error_type']}`; - this.serverErrorMessage = `error message : ${response.data['message']}`; + this.serverErrorType = `error type : ${response.data.error_type}`; + this.serverErrorMessage = `error message : ${response.data.message}`; }) .finally(() => { this.pending = false; @@ -728,6 +748,9 @@ Controller.prototype.save = function() { /** */ Controller.prototype.cancel = function() { + if (!this.features) { + throw new Error('Missing features'); + } this.dirty = false; this.feature = null; this.features.clear(); @@ -778,6 +801,9 @@ Controller.prototype.checkForModifications_ = function( /** */ Controller.prototype.continueWithoutSaving = function() { + if (!this.confirmDeferred_) { + throw new Error('Missing confirmDeferred_'); + } this.cancel(); this.confirmDeferred_.resolve(); }; @@ -786,6 +812,12 @@ Controller.prototype.continueWithoutSaving = function() { /** */ Controller.prototype.delete = function() { + if (!this.editableNode_) { + throw new Error('Missing editableNode_'); + } + if (!this.feature) { + throw new Error('Missing feature'); + } const msg = this.gettextCatalog_.getString( 'Do you really want to delete the selected feature?'); // Confirm deletion first @@ -798,6 +830,9 @@ Controller.prototype.delete = function() { this.feature ).then( (response) => { + if (!this.editableWMSLayer_) { + throw new Error('Missing editableWMSLayer_'); + } this.dirty = false; this.pending = false; this.ngeoLayerHelper_.refreshWMSLayer(this.editableWMSLayer_); @@ -808,8 +843,8 @@ Controller.prototype.delete = function() { (response) => { this.showServerError = true; this.pending = false; - this.serverErrorType = `error type : ${response.data['error_type']}`; - this.serverErrorMessage = `error message : ${response.data['message']}`; + this.serverErrorType = `error type : ${response.data.error_type}`; + this.serverErrorMessage = `error message : ${response.data.message}`; } ); @@ -838,6 +873,12 @@ Controller.prototype.submit = function() { Controller.prototype.handleEditFeature_ = function(resp) { const features = new olFormatGeoJSON().readFeatures(resp.data); if (features.length) { + if (!this.editableWMSLayer_) { + throw new Error('Missing editableWMSLayer_'); + } + if (!this.feature) { + throw new Error('Missing feature'); + } this.feature.setId(features[0].getId()); this.ngeoLayerHelper_.refreshWMSLayer(this.editableWMSLayer_); } @@ -883,7 +924,9 @@ Controller.prototype.handleFeatureAdd_ = function(evt) { if (evt instanceof CollectionEvent) { this.feature = null; this.timeout_(() => { - console.assert(this.attributes); + if (!this.attributes) { + throw new Error('Missing attributes'); + } const feature = evt.element; console.assert(feature instanceof olFeature); const dateFormatter = new DateFormatter(); @@ -930,6 +973,21 @@ Controller.prototype.handleFeatureAdd_ = function(evt) { * @private */ Controller.prototype.toggle_ = function(active) { + if (!this.modifyToolActivate) { + throw new Error('Missing modifyToolActivate'); + } + if (!this.translateToolActivate) { + throw new Error('Missing translateToolActivate'); + } + if (!this.rotateToolActivate) { + throw new Error('Missing rotateToolActivate'); + } + if (!this.modify_) { + throw new Error('Missing modify'); + } + if (!this.editableTreeCtrl) { + throw new Error('Missing editableTreeCtrl'); + } const keys = this.listenerKeys_; const createUid = ['create-', olUtilGetUid(this)].join('-'); @@ -937,6 +995,12 @@ Controller.prototype.toggle_ = function(active) { const toolMgr = this.ngeoToolActivateMgr_; if (active) { + if (!this.translate_) { + throw new Error('Missing translate'); + } + if (!this.rotate_) { + throw new Error('Missing rotate'); + } // FIXME //this.registerInteractions_(); @@ -976,7 +1040,7 @@ Controller.prototype.toggle_ = function(active) { this.modify_.setActive(active); this.mapSelectActive = active; - this.editableTreeCtrl.properties['editing'] = active; + this.editableTreeCtrl.properties.editing = active; }; @@ -987,6 +1051,9 @@ Controller.prototype.toggle_ = function(active) { * @private */ Controller.prototype.handleMapSelectActiveChange_ = function(active) { + if (!this.map) { + throw new Error('Missing map'); + } const mapDiv = this.map.getViewport(); console.assert(mapDiv); @@ -1020,6 +1087,9 @@ Controller.prototype.handleMapSelectActiveChange_ = function(active) { */ Controller.prototype.handleMapClick_ = function(evt) { if (evt instanceof MapBrowserEvent) { + if (!this.map) { + throw new Error('Missing map'); + } const coordinate = evt.coordinate; const pixel = evt.pixel; @@ -1028,6 +1098,9 @@ Controller.prototype.handleMapClick_ = function(evt) { const feature = this.map.forEachFeatureAtPixel( pixel, (feature) => { + if (!this.features) { + throw new Error('Missing features'); + } let ret = null; if (this.features.getArray().includes(feature)) { ret = feature; @@ -1047,10 +1120,19 @@ Controller.prototype.handleMapClick_ = function(evt) { // (2) If a feature is being edited and has unsaved changes, show modal // to let the user decide what to do this.checkForModifications_(true).then(() => { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.editableNode_) { + throw new Error('Missing editableNode'); + } const map = this.map; const view = map.getView(); const resolution = view.getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } const buffer = resolution * this.tolerance; const extent = olExtent.buffer( [coordinate[0], coordinate[1], coordinate[0], coordinate[1]], @@ -1079,12 +1161,18 @@ Controller.prototype.handleMapClick_ = function(evt) { */ Controller.prototype.handleMapContextMenu_ = function(evt) { if (evt instanceof Event) { + if (!this.map) { + throw new Error('Missing map'); + } const pixel = this.map.getEventPixel(evt); const coordinate = this.map.getCoordinateFromPixel(pixel); - let feature = /** @type {olFeature|undefined} */ (this.map.forEachFeatureAtPixel( + let feature = this.map.forEachFeatureAtPixel( pixel, (feature) => { + if (!this.features) { + throw new Error('Missing features'); + } let ret = null; if (this.features.getArray().includes(feature)) { ret = feature; @@ -1095,7 +1183,7 @@ Controller.prototype.handleMapContextMenu_ = function(evt) { hitTolerance: 7, layerFilter: undefined } - )); + ); feature = feature ? feature : null; @@ -1104,10 +1192,13 @@ Controller.prototype.handleMapContextMenu_ = function(evt) { this.vertexInfo_ = null; // show contextual menu when clicking on certain types of features - if (feature) { - + if (feature instanceof olFeature) { + const resolutions = this.map.getView().getResolution(); + if (!resolutions) { + throw new Error('Missing resolutions'); + } const vertexInfo = this.ngeoFeatureHelper_.getVertexInfoAtCoordinate( - feature, coordinate, this.map.getView().getResolution()); + feature, coordinate, resolutions); if (vertexInfo) { this.vertexInfo_ = vertexInfo; this.menuVertex_.open(coordinate); @@ -1138,6 +1229,9 @@ Controller.prototype.handleGetFeatures_ = function(features) { this.pending = false; this.timeout_(() => { + if (!this.features) { + throw new Error('Missing features'); + } if (features.length) { const feature = features[0]; this.feature = feature; @@ -1165,6 +1259,9 @@ Controller.prototype.initializeInteractions_ = function() { */ Controller.prototype.registerInteractions_ = function() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(interaction); }); }; @@ -1176,6 +1273,9 @@ Controller.prototype.registerInteractions_ = function() { */ Controller.prototype.unregisterInteractions_ = function() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.removeInteraction(interaction); }); }; @@ -1192,7 +1292,9 @@ Controller.prototype.handleFeatureChange_ = function(newFeature, oldFeature) { if (oldFeature) { olEvents.unlisten(oldFeature, 'propertychange', this.handleFeaturePropertyChange_, this); geom = oldFeature.getGeometry(); - console.assert(geom); + if (!geom) { + throw new Error('Missing geom'); + } olEvents.unlisten( geom, 'change', @@ -1206,7 +1308,9 @@ Controller.prototype.handleFeatureChange_ = function(newFeature, oldFeature) { this.featureId = newFeature.getId(); olEvents.listen(newFeature, 'propertychange', this.handleFeaturePropertyChange_, this); geom = newFeature.getGeometry(); - console.assert(geom); + if (!geom) { + throw new Error('Missing geom'); + } olEvents.listen( geom, 'change', @@ -1260,10 +1364,16 @@ Controller.prototype.handleMenuActionClick_ = function(evt) { switch (action) { case 'move': + if (!this.translate_) { + throw new Error('Missing translate'); + } this.translate_.setActive(true); this.scope_.$apply(); break; case 'rotate': + if (!this.rotate_) { + throw new Error('Missing rotate'); + } this.rotate_.setActive(true); this.scope_.$apply(); break; @@ -1282,6 +1392,12 @@ Controller.prototype.handleMenuVertexActionClick_ = function(evt) { switch (action) { case 'delete': + if (!this.feature) { + throw new Error('Missing feature'); + } + if (!this.vertexInfo_) { + throw new Error('Missing vertexInfo'); + } const feature = this.feature; const vertexInfo = this.vertexInfo_; this.ngeoFeatureHelper_.removeVertex(feature, vertexInfo); @@ -1298,6 +1414,9 @@ Controller.prototype.handleMenuVertexActionClick_ = function(evt) { * @private */ Controller.prototype.handleTranslateEnd_ = function(evt) { + if (!this.translate_) { + throw new Error('Missing translate'); + } this.translate_.setActive(false); this.scope_.$apply(); }; @@ -1308,6 +1427,9 @@ Controller.prototype.handleTranslateEnd_ = function(evt) { * @private */ Controller.prototype.handleRotateEnd_ = function(evt) { + if (!this.rotate_) { + throw new Error('Missing rotate'); + } this.rotate_.setActive(false); this.scope_.$apply(); }; @@ -1317,6 +1439,9 @@ Controller.prototype.handleRotateEnd_ = function(evt) { * @private */ Controller.prototype.handleDestroy_ = function() { + if (!this.features) { + throw new Error('Missing features'); + } this.features.clear(); this.handleFeatureChange_(null, this.feature); this.feature = null; diff --git a/contribs/gmf/src/editing/editFeatureSelectorComponent.js b/contribs/gmf/src/editing/editFeatureSelectorComponent.js index d5cff7dac68..276fcaeb426 100644 --- a/contribs/gmf/src/editing/editFeatureSelectorComponent.js +++ b/contribs/gmf/src/editing/editFeatureSelectorComponent.js @@ -105,25 +105,25 @@ function Controller($scope, $timeout, gmfThemes, gmfTreeManager) { ); /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {number|undefined} + * @type {?number} */ - this.tolerance; + this.tolerance = null; /** - * @type {import("ol/layer/Vector.js").default} + * @type {?import("ol/layer/Vector.js").default} */ - this.vectorLayer; + this.vectorLayer = null; /** * @type {boolean} * @export */ - this.closeAfterSave; + this.closeAfterSave = false; // === Injected services === @@ -163,6 +163,9 @@ function Controller($scope, $timeout, gmfThemes, gmfTreeManager) { const editables = this.editableTreeCtrls; editables.length = 0; + if (!this.gmfTreeManager_.rootCtrl) { + throw new Error('Missing gmfTreeManager_.rootCtrl'); + } this.gmfTreeManager_.rootCtrl.traverseDepthFirst((treeCtrl) => { if (treeCtrl.node.editable) { console.assert(treeCtrl.children.length === 0); @@ -181,6 +184,7 @@ function Controller($scope, $timeout, gmfThemes, gmfTreeManager) { if (gmfTreeManager.rootCtrl) { return gmfTreeManager.rootCtrl.children; } + return []; }, updateEditableTreeCtrls); diff --git a/contribs/gmf/src/filters/filterselectorComponent.js b/contribs/gmf/src/filters/filterselectorComponent.js index 60a6f4e983a..aba5db31c12 100644 --- a/contribs/gmf/src/filters/filterselectorComponent.js +++ b/contribs/gmf/src/filters/filterselectorComponent.js @@ -54,15 +54,15 @@ module.value('gmfFilterselectorTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfFilterselectorTemplateUrl']; + const templateUrl = $attrs.gmfFilterselectorTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/filters/filterselectorcomponent'; }); /** - * @param {!angular.IAttributes} $attrs Attributes. - * @param {!function(!angular.IAttributes): string} gmfFilterselectorTemplateUrl Template function. + * @param {angular.IAttributes} $attrs Attributes. + * @param {function(angular.IAttributes): string} gmfFilterselectorTemplateUrl Template function. * @return {string} Template URL. * @ngInject * @private @@ -80,8 +80,8 @@ function gmfFilterselectorTemplateUrl($attrs, gmfFilterselectorTemplateUrl) { class Controller { /** - * @param {!angular.IScope} $scope Angular scope. - * @param {!angular.ITimeoutService} $timeout Angular timeout service. + * @param {angular.IScope} $scope Angular scope. + * @param {angular.ITimeoutService} $timeout Angular timeout service. * @param {angular.gettext.gettextCatalog} gettextCatalog Gettext catalog. * @param {import('gmf/datasource/DataSourceBeingFiltered.js').DataSourceBeingFiltered} gmfDataSourceBeingFiltered * The Gmf value service that determines the data source currently being filtered. @@ -91,9 +91,9 @@ class Controller { * @param {import('gmf/authentication/Service.js').User} gmfUser User. * @param {import("ngeo/message/Notification.js").MessageNotification} ngeoNotification Ngeo notification * service. - * @param {!import("ngeo/map/FeatureOverlayMgr.js").FeatureOverlayMgr} ngeoFeatureOverlayMgr Ngeo + * @param {import("ngeo/map/FeatureOverlayMgr.js").FeatureOverlayMgr} ngeoFeatureOverlayMgr Ngeo * FeatureOverlay manager - * @param {!import("ngeo/filter/RuleHelper.js").RuleHelper} ngeoRuleHelper Ngeo rule helper service. + * @param {import("ngeo/filter/RuleHelper.js").RuleHelper} ngeoRuleHelper Ngeo rule helper service. * @private * @ngInject * @ngdoc controller @@ -109,7 +109,7 @@ class Controller { /** * @type {boolean} */ - this.active; + this.active = false; $scope.$watch( () => this.active, @@ -117,14 +117,14 @@ class Controller { ); /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {string} + * @type {?string} */ - this.toolGroup; + this.toolGroup = null; // Injected properties @@ -228,7 +228,7 @@ class Controller { this.filtrableDataSources = []; /** - * @type {Array.} + * @type {?Array} * @private */ this.filtrableLayerNodeNames_ = null; @@ -297,10 +297,10 @@ class Controller { /** * The name of the data source that should be automatically selected * by this component. - * @type {string|undefined} + * @type {?string} * @private */ - this.defaultFiltrableDataSourceName_; + this.defaultFiltrableDataSourceName_ = null; // Initialize the data sources registration this.toggleDataSourceRegistration_(); @@ -320,7 +320,7 @@ class Controller { if (usrFunc && usrFunc.preset_layer_filter && usrFunc.preset_layer_filter[0]) { this.defaultFiltrableDataSourceName_ = usrFunc.preset_layer_filter[0]; } else { - this.defaultFiltrableDataSourceName_ = undefined; + this.defaultFiltrableDataSourceName_ = null; } this.toggleDataSourceRegistration_(); } @@ -483,6 +483,9 @@ class Controller { * @private */ isDataSourceFiltrable_(dataSource, opt_notify) { + if (!this.filtrableLayerNodeNames_) { + throw new Error('Missing filtrableLayerNodeNames'); + } let filtrable = true; const gettext = this.gettextCatalog_; const notify = opt_notify !== false; @@ -623,6 +626,9 @@ class Controller { /** */ saveFilterSave() { + if (!this.readyDataSource) { + throw new Error('Missing readyDataSource'); + } const name = this.saveFilterName; const dataSource = this.readyDataSource; @@ -662,7 +668,9 @@ class Controller { * @param {!import("gmf/filters/SavedFilters.js").SavedFilterItem} filterItem Filter item. */ saveFilterLoadItem(filterItem) { - + if (!this.readyDataSource) { + throw new Error('Missing readyDataSource'); + } const dataSource = this.readyDataSource; // (1) Reset current rules @@ -686,6 +694,9 @@ class Controller { // (4) Update cache item const cacheItem = this.getRuleCacheItem_(dataSource); + if (!cacheItem) { + throw new Error('Missing cacheItem'); + } cacheItem.customRules = customRules; cacheItem.directedRules = directedRules; }); diff --git a/contribs/gmf/src/import/importdatasourceComponent.js b/contribs/gmf/src/import/importdatasourceComponent.js index 3eb8ac7422d..ac717e421f9 100644 --- a/contribs/gmf/src/import/importdatasourceComponent.js +++ b/contribs/gmf/src/import/importdatasourceComponent.js @@ -46,7 +46,7 @@ module.value('gmfImportdatasourceTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfImportdatasourceTemplateUrl']; + const templateUrl = $attrs.gmfImportdatasourceTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/import/importdatasourceComponent'; }); @@ -101,39 +101,39 @@ class Controller { // Binding properties /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; // Injected properties /** - * @type {!JQuery} + * @type {JQuery} * @private */ this.element_ = $element; /** - * @type {!angular.IScope} + * @type {angular.IScope} * @private */ this.scope_ = $scope; /** - * @type {!angular.ITimeoutService} + * @type {angular.ITimeoutService} * @private */ this.timeout_ = $timeout; /** - * @type {!import("gmf/datasource/ExternalDataSourcesManager.js").ExternalDatSourcesManager} + * @type {import("gmf/datasource/ExternalDataSourcesManager.js").ExternalDatSourcesManager} * @private */ this.gmfExternalDataSourcesManager_ = gmfExternalDataSourcesManager; /** - * @type {!import("ngeo/query/Querent.js").Querent} + * @type {import("ngeo/query/Querent.js").Querent} * @private */ this.ngeoQuerent_ = ngeoQuerent; @@ -142,14 +142,14 @@ class Controller { // Model properties /** - * @type {File|undefined} + * @type {?File} */ - this.file; + this.file = null; /** - * @type {string|undefined} + * @type {?string} */ - this.url; + this.url = null; // Inner properties @@ -206,18 +206,17 @@ class Controller { this.wmtsCapabilities = null; /** - * @type {Bloodhound|undefined} + * @type {?Bloodhound} * @private */ - this.serversEngine_; + this.serversEngine_ = null; - const servers = $injector.has('gmfExternalOGCServers') ? - /** @type {Array.|undefined} */ ( - $injector.get('gmfExternalOGCServers') - ) : undefined; + /** @type {?Array} */ + const servers = $injector.has('gmfExternalOGCServers') ? $injector.get('gmfExternalOGCServers') + : null; if (servers) { - const serverUrls = servers.map(server => server['url']); + const serverUrls = servers.map(server => server.url); this.serversEngine_ = new Bloodhound({ /** * Allows search queries to match from string from anywhere within @@ -258,7 +257,7 @@ class Controller { this.fileInput_.on('change', () => { const fileInput = /** @type HTMLInputElement */ (this.fileInput_[0]); const files = fileInput.files; - this.file = files && files[0] ? files[0] : undefined; + this.file = files && files[0] ? files[0] : null; this.scope_.$apply(); }); } @@ -274,7 +273,9 @@ class Controller { // Timeout to let Angular render the placeholder of the input properly, // otherwise typeahead would copy the string with {{}} in it... this.timeout_(() => { - console.assert(this.serversEngine_); + if (!this.serversEngine_) { + throw new Error('Missing serversEngine'); + } const $urlInput = this.element_.find('input[name=url]'); const $connectBtn = this.element_.find('button.gmf-importdatasource-connect-btn'); $urlInput.typeahead({ @@ -307,6 +308,9 @@ class Controller { * Connect to given online resource URL. */ connect() { + if (!this.url) { + throw new Error('Missing url'); + } const url = this.url; const serviceType = guessServiceTypeByUrl(url); @@ -345,6 +349,9 @@ class Controller { * Create data source from file. */ load() { + if (!this.file) { + throw new Error('Missing file'); + } const file = this.file; this.gmfExternalDataSourcesManager_.createAndAddDataSourceFromFile(file, (success) => { if (!success) { @@ -357,6 +364,9 @@ class Controller { * @return {string} The name of the file and human-readable size. */ get fileNameAndSize() { + if (!this.file) { + return ''; + } let nameAndSize = ''; const file = this.file; diff --git a/contribs/gmf/src/import/wmsCapabilityLayertreeComponent.js b/contribs/gmf/src/import/wmsCapabilityLayertreeComponent.js index ea49ab3a2b2..9fa4dbff3bc 100644 --- a/contribs/gmf/src/import/wmsCapabilityLayertreeComponent.js +++ b/contribs/gmf/src/import/wmsCapabilityLayertreeComponent.js @@ -33,7 +33,7 @@ module.value('gmfWmscapabilitylayertreenodeTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfWmscapabilitylayertreenodeTemplateUrl']; + const templateUrl = $attrs.gmfWmscapabilitylayertreenodeTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/import/wmsCapabilityLayertreeComponent'; }); @@ -74,28 +74,28 @@ class Controller { /** * WMS Capabilities definition - * @type {!Object} + * @type {Object} */ - this.capabilities; + this.capabilities = {}; /** * WMS Capability Layer object. - * @type {!Object} + * @type {Object} */ - this.layer; + this.layer = {}; /** * The original server url that was used to build the WMS GetCapabilities * request. - * @type {string} + * @type {?string} */ - this.url; + this.url = null; // Injected properties /** - * @type {!import("gmf/datasource/ExternalDataSourcesManager.js").ExternalDatSourcesManager} + * @type {import("gmf/datasource/ExternalDataSourcesManager.js").ExternalDatSourcesManager} * @private */ this.gmfExternalDataSourcesManager_ = gmfExternalDataSourcesManager; @@ -105,6 +105,9 @@ class Controller { * @param {!Object} layer WMS Capability Layer object */ createAndAddDataSource(layer) { + if (!this.url) { + throw new Error('Missing url'); + } this.gmfExternalDataSourcesManager_.createAndAddDataSourceFromWMSCapability( layer, this.capabilities, diff --git a/contribs/gmf/src/import/wmtsCapabilityLayertreeComponent.js b/contribs/gmf/src/import/wmtsCapabilityLayertreeComponent.js index ad0c9abb907..81dfafbfe8a 100644 --- a/contribs/gmf/src/import/wmtsCapabilityLayertreeComponent.js +++ b/contribs/gmf/src/import/wmtsCapabilityLayertreeComponent.js @@ -31,9 +31,8 @@ module.value('gmfWmtscapabilitylayertreTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfWmtscapabilitylayertreTemplateUrl']; - return templateUrl !== undefined ? templateUrl : - 'ngeo/import/wmtsCapabilityLayertreeComponent'; + const templateUrl = $attrs.gmfWmtscapabilitylayertreTemplateUrl; + return templateUrl !== undefined ? templateUrl : 'ngeo/import/wmtsCapabilityLayertreeComponent'; }); @@ -71,22 +70,22 @@ class Controller { /** * WMS Capabilities definition - * @type {!Object} + * @type {Object} */ - this.capabilities; + this.capabilities = {}; /** * List of WMTS Capability Layer objects. - * @type {!Array.} + * @type {Array} */ - this.layers; + this.layers = []; /** * The original WMTS GetCapabilities url that was used to fetch the * capability layers. - * @type {string} + * @type {?string} */ - this.url; + this.url = null; // Injected properties @@ -102,6 +101,9 @@ class Controller { * @param {!Object} layer WMTS Capability Layer object */ createAndAddDataSource(layer) { + if (!this.url) { + throw new Error('Missing url'); + } const manager = this.gmfExternalDataSourcesManager_; manager.createAndAddDataSourceFromWMTSCapability( layer, diff --git a/contribs/gmf/src/layertree/SyncLayertreeMap.js b/contribs/gmf/src/layertree/SyncLayertreeMap.js index 2fe128ef8fe..413135146b0 100644 --- a/contribs/gmf/src/layertree/SyncLayertreeMap.js +++ b/contribs/gmf/src/layertree/SyncLayertreeMap.js @@ -37,10 +37,10 @@ export function SyncLayertreeMap($rootScope, ngeoLayerHelper, ngeoWMSTime, gmfTh this.ngeoWMSTime_ = ngeoWMSTime; /** - * @type {import('gmf/themes.js').GmfOgcServers} + * @type {?import('gmf/themes.js').GmfOgcServers} * @private */ - this.ogcServersObject_; + this.ogcServersObject_ = null; gmfThemes.getOgcServersObject().then((ogcServersObject) => { this.ogcServersObject_ = ogcServersObject; @@ -62,12 +62,12 @@ export function SyncLayertreeMap($rootScope, ngeoLayerHelper, ngeoWMSTime, gmfTh * level group layer. * @param {number=} opt_position for first level Group, you can precise the * position to add the group in the array of layers of the dataLayerGroup. - * @return {import("ol/layer/Base.js").default|import("ol/layer/Group.js").default} a new layer. + * @return {?import("ol/layer/Base.js").default|import("ol/layer/Group.js").default} a new layer. * @public */ SyncLayertreeMap.prototype.createLayer = function(treeCtrl, map, dataLayerGroup, opt_position) { /** - * @type {import("ol/layer/Base.js").default|import("ol/layer/Group.js").default} + * @type {?import("ol/layer/Base.js").default|import("ol/layer/Group.js").default} */ let layer = null; if (treeCtrl.node.children !== undefined && treeCtrl.node.mixed) { @@ -180,7 +180,7 @@ SyncLayertreeMap.prototype.createGroup_ = function(treeCtrl, map, if (isFirstLevelGroup) { // First level group layer = this.createLayerFromGroup_(treeCtrl, !!groupNode.mixed); // Insert the layer at the right place - const position = opt_position | 0; + const position = opt_position || 0; dataLayerGroup.getLayers().insertAt(position, layer); } else { // Other Groups, create a group layer only in mixed groups @@ -193,6 +193,9 @@ SyncLayertreeMap.prototype.createGroup_ = function(treeCtrl, map, } } + if (!layer) { + throw new Error('Missing layer'); + } layer.set('printNativeAngle', printNativeAngle); return layer; }; @@ -206,13 +209,15 @@ SyncLayertreeMap.prototype.createGroup_ = function(treeCtrl, map, * @return {import("ol/layer/Image.js").default|import("ol/layer/Group.js").default} a new layer. * @private */ -SyncLayertreeMap.prototype.createLayerFromGroup_ = function(treeCtrl, - mixed) { +SyncLayertreeMap.prototype.createLayerFromGroup_ = function(treeCtrl, mixed) { let layer; const groupNode = /** @type {import('gmf/themes.js').GmfGroup} */ (treeCtrl.node); if (mixed) { // Will be one ol.layer per each node. layer = this.layerHelper_.createBasicGroup(); } else { // Will be one ol.layer for multiple WMS nodes. + if (!this.ogcServersObject_) { + throw new Error('Missing ogcServersObject'); + } const timeParam = this.getTimeParam_(treeCtrl); const ogcServer = this.ogcServersObject_[groupNode.ogcServer || '']; console.assert(ogcServer); @@ -260,9 +265,12 @@ SyncLayertreeMap.prototype.createLeafInAMixedGroup_ = function(treeCtrl, map) { if (gmfLayer.type === 'WMTS') { layer = this.createWMTSLayer_(/** @type import('gmf/themes.js').GmfLayerWMTS */ (gmfLayer)); } else { + if (!this.ogcServersObject_) { + throw new Error('Missing ogcServersObject'); + } const gmfLayerWMS = /** @type import('gmf/themes.js').GmfLayerWMS */ (gmfLayer); const timeParam = this.getTimeParam_(treeCtrl); - const ogcServer = this.ogcServersObject_[/** @type string */ (gmfLayerWMS.ogcServer)]; + const ogcServer = this.ogcServersObject_[gmfLayerWMS.ogcServer]; console.assert(ogcServer); console.assert(ogcServer.url); console.assert(ogcServer.type); @@ -385,7 +393,7 @@ SyncLayertreeMap.prototype.getTimeParam_ = function(treeCtrl) { }); } if (wmsTime) { - const timeValues = this.ngeoWMSTime_.getOptions(wmsTime)['values']; + const timeValues = this.ngeoWMSTime_.getOptions(wmsTime).values; timeParam = this.ngeoWMSTime_.formatWMSTimeParam(wmsTime, { start: timeValues[0] || timeValues, end: timeValues[1] @@ -444,6 +452,9 @@ export function getLayer(treeCtrl) { } tree = tree.parent; } + if (!layer) { + throw new Error('Missing layer'); + } return layer; } diff --git a/contribs/gmf/src/layertree/TreeManager.js b/contribs/gmf/src/layertree/TreeManager.js index 9b142ee1de5..4d484cf78bf 100644 --- a/contribs/gmf/src/layertree/TreeManager.js +++ b/contribs/gmf/src/layertree/TreeManager.js @@ -1,7 +1,8 @@ import angular from 'angular'; import {PermalinkParam} from 'gmf/index.js'; import gmfThemeThemes, {findGroupByName, findGroupByLayerNodeName} from 'gmf/theme/Themes.js'; -import ngeoLayertreeController, {LayertreeVisitorDecision} from 'ngeo/layertree/Controller.js'; +import ngeoLayertreeController, {LayertreeVisitorDecision, LayertreeController} from + 'ngeo/layertree/Controller.js'; import {MessageType} from 'ngeo/message/Message.js'; import ngeoMessageNotification from 'ngeo/message/Notification.js'; import ngeoStatemanagerService from 'ngeo/statemanager/Service.js'; @@ -11,9 +12,9 @@ import * as olEvents from 'ol/events.js'; /** * @typedef {Object} TreeManagerFullState * @property {Object.} [children] - * @property {boolean} [isChecked] - * @property {boolean} [isExpanded] - * @property {boolean} [isLegendExpanded] + * @property {boolean|undefined} [isChecked] + * @property {boolean|undefined} [isExpanded] + * @property {boolean|undefined} [isLegendExpanded] */ @@ -97,7 +98,7 @@ export function LayertreeTreeManager($timeout, $injector, gettextCatalog, ngeoLa * The controller of the (unique) root layer tree. * The array of top level layer trees is available through `rootCtrl.children`. * The order doesn't match with the ordre of the displayed layertree. - * @type {import("ngeo/layertree/Controller.js").LayertreeController} + * @type {?import("ngeo/layertree/Controller.js").LayertreeController} */ this.rootCtrl = null; @@ -116,7 +117,7 @@ export function LayertreeTreeManager($timeout, $injector, gettextCatalog, ngeoLa this.groupsToAddInThisDigestLoop_ = []; /** - * @type {angular.IPromise} + * @type {?angular.IPromise} * @private */ this.promiseForGroupsToAddInThisDigestLoop_ = null; @@ -129,7 +130,7 @@ export function LayertreeTreeManager($timeout, $injector, gettextCatalog, ngeoLa /** * A reference to the OGC servers loaded by the theme service. - * @type {import('gmf/themes.js').GmfOgcServers|null} + * @type {?import('gmf/themes.js').GmfOgcServers} * @private */ this.ogcServers_ = null; @@ -225,13 +226,17 @@ LayertreeTreeManager.prototype.addFirstLevelGroup_ = function(group) { const groupID = group.id; this.root.children.some((rootChild) => { if (groupID === rootChild.id) { - return alreadyAdded = true; + alreadyAdded = true; + return true; } + return false; }, this); this.groupsToAddInThisDigestLoop_.some((grp) => { if (groupID === grp.id) { - return alreadyAdded = true; + alreadyAdded = true; + return true; } + return false; }, this); if (alreadyAdded) { return false; @@ -344,7 +349,8 @@ LayertreeTreeManager.prototype.addGroupByLayerName = function(layerName, opt_add console.warn('Tree controller not found, unable to add the group'); return; } - let treeCtrlToActive = /** @type import("ngeo/layertree/Controller.js").LayertreeController */ (null); + /** @type {any} */ + let treeCtrlToActive = null; treeCtrl.traverseDepthFirst((treeCtrl) => { if (treeCtrl.node.name === layerName) { treeCtrlToActive = treeCtrl; @@ -358,7 +364,7 @@ LayertreeTreeManager.prototype.addGroupByLayerName = function(layerName, opt_add } // Active it. - if (treeCtrlToActive) { + if (treeCtrlToActive instanceof LayertreeController) { treeCtrlToActive.setState('on'); } }); @@ -380,6 +386,7 @@ LayertreeTreeManager.prototype.removeGroup = function(group) { return found = true; } index++; + return false; }); if (found) { children.splice(index, 1); @@ -490,6 +497,9 @@ LayertreeTreeManager.prototype.getTreeCtrlByNodeId = function(id) { * @return {import('gmf/themes.js').GmfOgcServer} The OGC server. */ LayertreeTreeManager.prototype.getOgcServer = function(treeCtrl) { + if (!this.ogcServers_) { + throw new Error('Missing ogcServers'); + } if (treeCtrl.parent.node.mixed) { const gmfLayerWMS = /** @type {import('gmf/themes.js').GmfLayerWMS} */ (treeCtrl.node); console.assert(gmfLayerWMS.ogcServer); @@ -516,6 +526,9 @@ LayertreeTreeManager.prototype.getOgcServer = function(treeCtrl) { * @private */ LayertreeTreeManager.prototype.refreshFirstLevelGroups_ = function(themes) { + if (!this.rootCtrl) { + throw new Error('Missing rootCtrl'); + } const firstLevelGroupsFullState = {}; // Save state of each child diff --git a/contribs/gmf/src/layertree/component.js b/contribs/gmf/src/layertree/component.js index 1729e8871c1..56bad421752 100644 --- a/contribs/gmf/src/layertree/component.js +++ b/contribs/gmf/src/layertree/component.js @@ -27,6 +27,7 @@ import {isEmpty} from 'ol/obj.js'; import olSourceImageWMS from 'ol/source/ImageWMS.js'; import olSourceTileWMS from 'ol/source/TileWMS.js'; import olSourceWMTS from 'ol/source/WMTS.js'; +import LayerBase from 'ol/layer/Base.js'; import 'bootstrap/js/src/collapse.js'; @@ -190,21 +191,21 @@ function Controller($element, $scope, ngeoLayerHelper, /** * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {?Object} */ - this.dimensions; + this.dimensions = null; /** - * @type {!angular.IScope} + * @type {angular.IScope} * @private */ this.scope_ = $scope; /** - * @type {!import("ngeo/map/LayerHelper.js").LayerHelper} + * @type {import("ngeo/map/LayerHelper.js").LayerHelper} * @private */ this.layerHelper_ = ngeoLayerHelper; @@ -215,18 +216,18 @@ function Controller($element, $scope, ngeoLayerHelper, this.gmfDataSourceBeingFiltered = gmfDataSourceBeingFiltered; /** - * @type {!import("gmf/datasource/ExternalDataSourcesManager.js").ExternalDatSourcesManager} + * @type {import("gmf/datasource/ExternalDataSourcesManager.js").ExternalDatSourcesManager} */ this.gmfExternalDataSourcesManager = gmfExternalDataSourcesManager; /** - * @type {!import("gmf/permalink/Permalink.js").PermalinkService} + * @type {import("gmf/permalink/Permalink.js").PermalinkService} * @private */ this.gmfPermalink_ = gmfPermalink; /** - * @type {!import("gmf/layertree/TreeManager.js").LayertreeTreeManager} + * @type {import("gmf/layertree/TreeManager.js").LayertreeTreeManager} * @private */ this.gmfTreeManager_ = gmfTreeManager; @@ -235,32 +236,32 @@ function Controller($element, $scope, ngeoLayerHelper, console.assert(root); /** - * @type {!import('gmf/themes.js').GmfRootNode} + * @type {import('gmf/themes.js').GmfRootNode} */ this.root = root; /** - * @type {!import("gmf/layertree/SyncLayertreeMap.js").SyncLayertreeMap} + * @type {import("gmf/layertree/SyncLayertreeMap.js").SyncLayertreeMap} * @private */ this.gmfSyncLayertreeMap_ = gmfSyncLayertreeMap; /** - * @type {!import("ngeo/misc/WMSTime.js").WMSTime} + * @type {import("ngeo/misc/WMSTime.js").WMSTime} * @private */ this.ngeoWMSTime_ = ngeoWMSTime; /** - * @type {!Object.>} + * @type {Object>} * @private */ this.groupNodeStates_ = {}; /** - * @type {boolean|undefined} + * @type {?boolean} */ - this.openLinksInNewWindow; + this.openLinksInNewWindow = null; /** * @type {?import("ol/layer/Group.js").default} @@ -269,12 +270,12 @@ function Controller($element, $scope, ngeoLayerHelper, this.dataLayerGroup_ = null; /** - * @type {!Array.} + * @type {Array} */ this.layers = []; /** - * @type {!import("gmf/theme/Themes.js").ThemesService} + * @type {import("gmf/theme/Themes.js").ThemesService} * @private */ this.gmfThemes_ = gmfThemes; @@ -290,6 +291,9 @@ function Controller($element, $scope, ngeoLayerHelper, * Init the controller, */ Controller.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } this.openLinksInNewWindow = this.openLinksInNewWindow === true; this.dataLayerGroup_ = this.layerHelper_.getGroupFromMap(this.map, DATALAYERGROUP_NAME); @@ -298,6 +302,9 @@ Controller.prototype.$onInit = function() { // watch any change on layers array to refresh the map this.scope_.$watchCollection(() => this.layers, () => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.render(); }); @@ -308,6 +315,9 @@ Controller.prototype.$onInit = function() { } }, (dimensions) => { if (dimensions) { + if (!this.gmfTreeManager_.rootCtrl) { + throw new Error('Missing gmfTreeManager_.rootCtrl'); + } this.updateDimensions_(this.gmfTreeManager_.rootCtrl); } }); @@ -380,6 +390,12 @@ Controller.prototype.updateLayerDimensions_ = function(layer, node) { * layer or group for the node. */ Controller.prototype.getLayer = function(treeCtrl) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.dataLayerGroup_) { + throw new Error('Missing dataLayerGroup'); + } let opt_position; if (treeCtrl.parent.isRoot) { this.gmfTreeManager_.rootCtrl = treeCtrl.parent; @@ -410,8 +426,14 @@ Controller.prototype.getLayer = function(treeCtrl) { * from the current node. */ Controller.prototype.listeners = function(scope, treeCtrl) { + if (!this.dataLayerGroup_) { + throw new Error('Missing dataLayerGroup'); + } const dataLayerGroup = this.dataLayerGroup_; scope.$on('$destroy', () => { + if (!treeCtrl.layer) { + throw new Error('Missing treeCtrl.layer'); + } // Remove the layer from the map. dataLayerGroup.getLayers().remove(treeCtrl.layer); }); @@ -522,11 +544,12 @@ Controller.prototype.getLegendIconURL = function(treeCtrl) { * Get the legends object ( for each layer) for the given treeCtrl. * @param {import("ngeo/layertree/Controller.js").LayertreeController} treeCtrl ngeo layertree controller, * from the current node. - * @return {Object.} A object that provides a + * @return {?Object} A object that provides a * layer for each layer. */ Controller.prototype.getLegendsObject = function(treeCtrl) { - const legendsObject = /** @type {Object.} */ ({}); + /** @type {Object} */ + const legendsObject = {}; if (/** @type import('gmf/themes.js').GmfGroup */ (treeCtrl.node).children !== undefined) { return null; } @@ -540,10 +563,13 @@ Controller.prototype.getLegendsObject = function(treeCtrl) { const layer = treeCtrl.layer; if (gmfLayer.type === 'WMTS') { - console.assert(layer instanceof olLayerTile); - const wmtsLegendURL = this.layerHelper_.getWMTSLegendURL( - /** @type {import("ol/layer/Tile.js").default} */ (layer)); - legendsObject[gmfLayerDefaultName] = wmtsLegendURL; + if (!(layer instanceof olLayerTile)) { + throw new Error('Wrong layer'); + } + const wmtsLegendURL = this.layerHelper_.getWMTSLegendURL(layer); + if (wmtsLegendURL !== undefined) { + legendsObject[gmfLayerDefaultName] = wmtsLegendURL; + } return wmtsLegendURL ? legendsObject : null; } else { const gmfLayerWMS = /** @type {import('gmf/themes.js').GmfLayerWMS} */ (gmfLayer); @@ -552,15 +578,16 @@ Controller.prototype.getLegendsObject = function(treeCtrl) { const scale = this.getScale_(); // QGIS can handle multiple layers natively. Use Multiple URLs for other map // servers - let layerNamesList; - if (gmfOgcServer.type === ServerType.QGISSERVER) { - layerNamesList = [layersNames]; - } else { - layerNamesList = layersNames.split(','); + if (gmfOgcServer.type !== ServerType.QGISSERVER) { + const layerNamesList = layersNames.split(','); + layerNamesList.forEach((layerName) => { + const wmtsLegendURL = this.layerHelper_.getWMSLegendURL(gmfOgcServer.url, layerName, scale); + if (!wmtsLegendURL) { + throw new Error('Missing wmtsLegendURL'); + } + legendsObject[layerName] = wmtsLegendURL; + }); } - layerNamesList.forEach((layerName) => { - legendsObject[layerName] = this.layerHelper_.getWMSLegendURL(gmfOgcServer.url, layerName, scale); - }); return legendsObject; } }; @@ -584,9 +611,18 @@ Controller.prototype.getNumberOfLegendsObject = function(treeCtrl) { * @private */ Controller.prototype.getScale_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } const view = this.map.getView(); const resolution = view.getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } const mpu = view.getProjection().getMetersPerUnit(); + if (!mpu) { + throw new Error('Missing mpu'); + } const dpi = 25.4 / 0.28; return resolution * mpu * 39.37 * dpi; }; @@ -599,7 +635,7 @@ Controller.prototype.getScale_ = function() { */ Controller.prototype.displayMetadata = function(treeCtrl) { const node = treeCtrl.node; - const metadataURL = node.metadata['metadataUrl']; + const metadataURL = node.metadata.metadataUrl; if (metadataURL !== undefined) { // FIXME layertree should not rely on a window function. // @ts-ignore: gmfx is available, see upper @@ -616,6 +652,9 @@ Controller.prototype.displayMetadata = function(treeCtrl) { * a reorder of the first-level groups. Then update the permalink. */ Controller.prototype.afterReorder = function() { + if (!this.gmfTreeManager_.rootCtrl) { + throw new Error('Missing gmfTreeManager_.rootCtrl'); + } const groupNodes = this.gmfTreeManager_.rootCtrl.node.children; const currentTreeCtrls = this.gmfTreeManager_.rootCtrl.children; const treeCtrls = []; @@ -630,6 +669,7 @@ Controller.prototype.afterReorder = function() { // is not the wanted behaviour... return false; } + return false; }); }); @@ -639,6 +679,9 @@ Controller.prototype.afterReorder = function() { // Update map 'data' groupe layers order this.layers.length = 0; this.gmfTreeManager_.rootCtrl.children.forEach((child) => { + if (!(child.layer instanceof LayerBase)) { + throw new Error('Wrong child.layer'); + } this.layers.push(child.layer); }); @@ -679,7 +722,13 @@ Controller.prototype.nodesCount = function() { * @return {string|undefined} 'out-of-resolution' or undefined. */ Controller.prototype.getResolutionStyle = function(gmfLayer) { + if (!this.map) { + throw new Error('Missing map'); + } const resolution = this.map.getView().getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } const minResolution = getNodeMinResolution(gmfLayer); if (minResolution !== undefined && resolution < minResolution) { return 'out-of-resolution'; @@ -698,9 +747,15 @@ Controller.prototype.getResolutionStyle = function(gmfLayer) { * from the current node. */ Controller.prototype.zoomToResolution = function(treeCtrl) { + if (!this.map) { + throw new Error('Missing map'); + } const gmfLayer = /** @type {import('gmf/themes.js').GmfLayerWMS} */ (treeCtrl.node); const view = this.map.getView(); const resolution = view.getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } const minResolution = getNodeMinResolution(gmfLayer); if (minResolution !== undefined && resolution < minResolution) { view.setResolution(view.constrainResolution(minResolution, 0, 1)); diff --git a/contribs/gmf/src/layertree/datasourceGroupTreeComponent.js b/contribs/gmf/src/layertree/datasourceGroupTreeComponent.js index 68331c84130..c89a2cfc31a 100644 --- a/contribs/gmf/src/layertree/datasourceGroupTreeComponent.js +++ b/contribs/gmf/src/layertree/datasourceGroupTreeComponent.js @@ -65,21 +65,21 @@ class Controller { // Binding properties /** - * @type {!import("ngeo/datasource/Group.js").default} + * @type {?import("ngeo/datasource/Group.js").default} */ - this.group; + this.group = null; // Injected properties /** - * @type {!angular.IScope} + * @type {angular.IScope} * @private */ this.scope_ = $scope; /** - * @type {!import('ngeo/datasource/DataSource.js').DataSources} + * @type {import('ngeo/datasource/DataSource.js').DataSources} * @private */ this.dataSources_ = ngeoDataSources.collection; @@ -96,6 +96,9 @@ class Controller { * Toggle visibility of the group itself, i.e. its visibility state. */ toggle() { + if (!this.group) { + throw new Error('Missing group'); + } this.group.toggleVisibilityState(); } @@ -114,6 +117,9 @@ class Controller { * is going to be removed as well, destroying this component in the process. */ remove() { + if (!this.group) { + throw new Error('Missing group'); + } for (let i = this.group.dataSources.length - 1, ii = 0; i >= ii; i--) { this.dataSources_.remove(this.group.dataSources[i]); } diff --git a/contribs/gmf/src/layertree/timeSliderComponent.js b/contribs/gmf/src/layertree/timeSliderComponent.js index a827d986e07..fd0b4f8be80 100644 --- a/contribs/gmf/src/layertree/timeSliderComponent.js +++ b/contribs/gmf/src/layertree/timeSliderComponent.js @@ -55,12 +55,18 @@ function layertreeTimeSliderComponent() { templateUrl: 'gmf/layertree/timesliderComponent', link: { pre: function preLink(scope, element, attrs, ctrl) { + if (!ctrl) { + throw new Error('Missing ctrl'); + } ctrl.init(); - ctrl.sliderOptions['stop'] = onSliderReleased_; - ctrl.sliderOptions['slide'] = computeDates_; + ctrl.sliderOptions.stop = onSliderReleased_; + ctrl.sliderOptions.slide = computeDates_; function onSliderReleased_(e, sliderUi) { + if (!ctrl) { + throw new Error('Missing ctrl'); + } ctrl.onDateSelected({ time: computeDates_(e, sliderUi) }); @@ -68,6 +74,9 @@ function layertreeTimeSliderComponent() { } function computeDates_(e, sliderUi) { + if (!ctrl) { + throw new Error('Missing ctrl'); + } let sDate, eDate, wmstime; if (sliderUi.values) { sDate = new Date(ctrl.getClosestValue_(sliderUi.values[0])); @@ -118,54 +127,54 @@ function Controller(ngeoWMSTime) { * Function called after date(s) changed/selected * @type {Function} */ - this.onDateSelected; + this.onDateSelected = () => undefined; /** * A time object for directive initialization - * @type {import('ngeo/datasource/OGC.js').TimeProperty} + * @type {?import('ngeo/datasource/OGC.js').TimeProperty} */ - this.time; + this.time = null; /** * If the component is used to select a date range * @type {boolean} */ - this.isModeRange; + this.isModeRange = false; /** * Minimal value of the slider (time in ms) * @type {number} */ - this.minValue; + this.minValue = -1; /** * Maximal value of the slider (time in ms) * @type {number} */ - this.maxValue; + this.maxValue = 999999; /** * Used when WMS time object has a property 'values' instead of an interval - * @type {?Array.} + * @type {?Array} */ - this.timeValueList; + this.timeValueList = null; /** * Default Slider options (used by ui-slider directive) - * @type {{ + * @type {?{ * range : boolean, * min : number, * max : number * }} */ - this.sliderOptions; + this.sliderOptions = null; /** * Model for the ui-slider directive (date in ms format) - * @type {Array.|number} + * @type {Array|number} */ - this.dates; + this.dates = []; } @@ -173,6 +182,9 @@ function Controller(ngeoWMSTime) { * Initialise the controller. */ Controller.prototype.init = function() { + if (!this.time) { + throw new Error('Missing time'); + } this.timeValueList = this.getTimeValueList_(); // Fetch the initial options for the component @@ -196,8 +208,11 @@ Controller.prototype.init = function() { * @return {Array} - List of timestamp representing possible values */ Controller.prototype.getTimeValueList_ = function() { + if (!this.time) { + throw new Error('Missing time'); + } const wmsTime = this.time; - let timeValueList = null; + let timeValueList = []; const minDate = new Date(this.minValue); const maxDate = new Date(this.maxValue); @@ -243,6 +258,9 @@ Controller.prototype.getTimeValueList_ = function() { * @private */ Controller.prototype.getClosestValue_ = function(timestamp) { + if (!this.time) { + throw new Error('Missing time'); + } if (timestamp <= this.minValue) { return this.minValue; } @@ -312,6 +330,9 @@ Controller.prototype.getClosestValue_ = function(timestamp) { * @return {string} Localized date string regarding the resolution. */ Controller.prototype.getLocalizedDate = function(time) { + if (!this.time) { + throw new Error('Missing time'); + } return this.ngeoWMSTime_.formatTimeValue(time, this.time.resolution); }; diff --git a/contribs/gmf/src/lidarprofile/Config.js b/contribs/gmf/src/lidarprofile/Config.js index ed899e903a6..e62b01521cc 100644 --- a/contribs/gmf/src/lidarprofile/Config.js +++ b/contribs/gmf/src/lidarprofile/Config.js @@ -116,7 +116,7 @@ export class LidarprofileConfigService { /** * The configuration from the LIDAR server. - * @type {import("gmf/lidarprofile/Config.js").LidarprofileServerConfig} + * @type {?import("gmf/lidarprofile/Config.js").LidarprofileServerConfig} */ this.serverConfig = null; } diff --git a/contribs/gmf/src/lidarprofile/Manager.js b/contribs/gmf/src/lidarprofile/Manager.js index 78ae5b16829..9168a797b65 100644 --- a/contribs/gmf/src/lidarprofile/Manager.js +++ b/contribs/gmf/src/lidarprofile/Manager.js @@ -60,22 +60,22 @@ export class LidarprofileManager { this.promise = null; /** - * @type {import("gmf/lidarprofile/Plot.js").default} + * @type {?import("gmf/lidarprofile/Plot.js").default} */ this.plot = null; /** - * @type {import("gmf/lidarprofile/Measure.js").default} + * @type {?import("gmf/lidarprofile/Measure.js").default} */ this.measure = null; /** - * @type {import("gmf/lidarprofile/Config.js").LidarprofileConfigService} + * @type {?import("gmf/lidarprofile/Config.js").LidarprofileConfigService} */ this.config = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} * @private */ this.map_ = null; @@ -128,10 +128,10 @@ export class LidarprofileManager { this.isPlotSetup_ = false; /** - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} * @private */ - this.line_; + this.line_ = null; /** * @type {import("gmf/lidarprofile/Utils.js").default} @@ -203,6 +203,18 @@ export class LidarprofileManager { * @param {number} minLOD minimum Level Of Detail */ getProfileByLOD(clippedLine, distanceOffset, resetPlot, minLOD) { + if (!this.config) { + throw new Error('Missing config'); + } + if (!this.plot) { + throw new Error('Missing plot'); + } + if (!this.line_) { + throw new Error('Missing line'); + } + if (!this.config.serverConfig) { + throw new Error('Missing config.serverConfig'); + } const gettextCatalog = this.gettextCatalog; this.profilePoints = this.getEmptyProfilePoints_(); @@ -277,6 +289,12 @@ export class LidarprofileManager { * @private */ queryPytree_(minLOD, maxLOD, iter, coordinates, distanceOffset, lastLOD, width, resetPlot) { + if (!this.config) { + throw new Error('Missing config'); + } + if (!this.config.serverConfig) { + throw new Error('Missing config.serverConfig'); + } const gettextCatalog = this.gettextCatalog; const lodInfo = d3select('#gmf-lidarprofile-container .lod-info'); if (this.config.serverConfig.debug) { @@ -296,6 +314,12 @@ export class LidarprofileManager { }, responseType: 'arraybuffer' }).then((response) => { + if (!this.config) { + throw new Error('Missing config'); + } + if (!this.config.serverConfig) { + throw new Error('Missing config.serverConfig'); + } if (this.config.serverConfig.debug) { let html = lodInfo.html(); const lodTxt = gettextCatalog.getString('LOD: '); @@ -319,6 +343,18 @@ export class LidarprofileManager { * @private */ processBuffer_(profile, iter, distanceOffset, lastLOD, resetPlot) { + if (!this.config) { + throw new Error('Missing config'); + } + if (!this.config.serverConfig) { + throw new Error('Missing config.serverConfig'); + } + if (!this.plot) { + throw new Error('Missing plot'); + } + if (!this.line_) { + throw new Error('Missing line'); + } const lidarError = d3select('#gmf-lidarprofile-container .lidar-error'); const typedArrayInt32 = new Int32Array(profile, 0, 4); @@ -465,6 +501,18 @@ export class LidarprofileManager { * @private */ updateData_() { + if (!this.config) { + throw new Error('Missing config'); + } + if (!this.config.serverConfig) { + throw new Error('Missing config.serverConfig'); + } + if (!this.plot) { + throw new Error('Missing plot'); + } + if (!this.line_) { + throw new Error('Missing line'); + } const domainX = this.plot.updateScaleX['domain'](); let map_resolution = this.map_ ? this.map_.getView().getResolution() : 0; map_resolution = map_resolution || 0; diff --git a/contribs/gmf/src/lidarprofile/Measure.js b/contribs/gmf/src/lidarprofile/Measure.js index 0c6ffe11fcc..b6269dd194c 100644 --- a/contribs/gmf/src/lidarprofile/Measure.js +++ b/contribs/gmf/src/lidarprofile/Measure.js @@ -66,6 +66,12 @@ export default class { * Measure and display height after two click on the profile. */ measureHeigt() { + if (!this.manager_.config) { + throw new Error('Missing manager.config'); + } + if (!this.manager_.plot) { + throw new Error('Missing manager.plot'); + } const svg = d3select('#gmf-lidarprofile-container svg.lidar-svg'); const svgCoordinates = d3mouse(svg.node()); const canvasCoordinates = d3mouse(d3select('#gmf-lidarprofile-container .lidar-canvas').node()); @@ -76,6 +82,9 @@ export default class { const sx = this.manager_.plot.updateScaleX; const sy = this.manager_.plot.updateScaleY; const pointSize = 3; + if (!this.manager_.config.serverConfig) { + throw new Error('Missing manager_.config.serverConfig'); + } const p = this.manager_.utils.getClosestPoint( this.manager_.profilePoints, canvasCoordinates[0], @@ -87,7 +96,7 @@ export default class { if (!this.pStart_.set) { - if (p !== undefined) { + if (p !== null) { this.pStart_.distance = p.distance; this.pStart_.altitude = p.altitude; this.pStart_.cx = sx(p.distance) + margin.left; @@ -108,7 +117,7 @@ export default class { .style('fill', 'red'); } else if (!this.pEnd_.set) { - if (p !== undefined) { + if (p !== null) { this.pEnd_.distance = p.distance; this.pEnd_.altitude = p.altitude; diff --git a/contribs/gmf/src/lidarprofile/Plot.js b/contribs/gmf/src/lidarprofile/Plot.js index 35a6fe6f59e..e62e9ce82c2 100644 --- a/contribs/gmf/src/lidarprofile/Plot.js +++ b/contribs/gmf/src/lidarprofile/Plot.js @@ -36,45 +36,45 @@ export default class { /** * d3.scaleLinear X scale. - * @type {d3.ScaleLinear} + * @type {?d3.ScaleLinear} */ - this.scaleX; + this.scaleX = null; /** * d3.scaleLinear X scale. * @type {Function} */ - this.updateScaleX; + this.updateScaleX = () => undefined; /** * d3.scaleLinear Y scale. - * @type {d3.ScaleLinear} + * @type {?d3.ScaleLinear} */ - this.scaleY; + this.scaleY = null; /** * d3.scaleLinear Y scale. * @type {Function} */ - this.updateScaleY; + this.updateScaleY = () => undefined; /** * The material used for the drawing process. Initialized in the setup - * @type {string} + * @type {?string} */ - this.material; + this.material = null; /** * @type {number} * @private */ - this.width_; + this.width_ = 0; /** * @type {number} * @private */ - this.height_; + this.height_ = 0; /** * @type {Array.} @@ -94,6 +94,12 @@ export default class { * @param {import("gmf/lidarprofile/Utils.js").LidarprofilePoints} points of the profile */ drawPoints(points) { + if (!this.manager_.config) { + throw new Error('Missing manager.config'); + } + if (!this.manager_.config.serverConfig) { + throw new Error('Missing manager_.config.serverConfig'); + } let i = -1; const nPoints = points.distance.length; let cx, cy; @@ -138,6 +144,12 @@ export default class { * @param {Array.} rangeY range of the y scale */ setupPlot(rangeX, rangeY) { + if (!this.manager_.config) { + throw new Error('Missing manager.config'); + } + if (!this.manager_.config.serverConfig) { + throw new Error('Missing manager_.config.serverConfig'); + } const canvas = d3select('#gmf-lidarprofile-container .lidar-canvas'); const canvasEl = canvas.node(); const ctx = canvasEl.getContext('2d'); @@ -199,7 +211,7 @@ export default class { zoom.on('end', this.zoomEnd.bind(this)); - this.previousDomainX = this.scaleX['domain'](); + this.previousDomainX = this.scaleX.domain(); this.updateScaleX = this.scaleX; this.updateScaleY = this.scaleY; @@ -260,6 +272,15 @@ export default class { * Update the plot axis during the zoom process */ zoomed() { + if (!this.manager_.measure) { + throw new Error('Missing manager.measure'); + } + if (!this.scaleX) { + throw new Error('Missing scaleX'); + } + if (!this.scaleY) { + throw new Error('Missing scaleY'); + } if (d3event.sourceEvent && d3event.sourceEvent.type === 'mousemove') { this.moved_ = true; if (d3event.sourceEvent.movementX == 0 && d3event.sourceEvent.movementY == 0) { @@ -299,6 +320,12 @@ export default class { * Update the Openlayers overlay that displays point position and attributes values */ pointHighlight() { + if (!this.manager_.config) { + throw new Error('Missing manager.config'); + } + if (!this.manager_.config.serverConfig) { + throw new Error('Missing manager_.config.serverConfig'); + } const svg = d3select('#gmf-lidarprofile-container svg.lidar-svg'); const lidarInfo = d3select('#gmf-lidarprofile-container .lidar-info'); @@ -335,7 +362,7 @@ export default class { const html = this.getInfoHTML(p, pointClassification, 1); lidarInfo.html(html); - this.manager_.cartoHighlight.setElement(null); + this.manager_.cartoHighlight.setElement(undefined); const el = document.createElement('div'); el.className += 'tooltip gmf-tooltip-measure'; el.innerHTML = html; @@ -430,6 +457,12 @@ export default class { * @param {string} material value as defined in Pytree attribute configuration */ setClassActive(classification, material) { + if (!this.manager_.config) { + throw new Error('Missing manager.config'); + } + if (!this.manager_.config.serverConfig) { + throw new Error('Missing manager_.config.serverConfig'); + } this.manager_.config.serverConfig.classification_colors = classification; this.changeStyle(material); } diff --git a/contribs/gmf/src/lidarprofile/Utils.js b/contribs/gmf/src/lidarprofile/Utils.js index 7676452acae..add2c9eb5e9 100644 --- a/contribs/gmf/src/lidarprofile/Utils.js +++ b/contribs/gmf/src/lidarprofile/Utils.js @@ -243,6 +243,9 @@ export default class { canvas.width = w; canvas.height = h; const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Missing ctx'); + } ctx.fillStyle = 'white'; ctx.fillRect(0, 0, w, h); @@ -265,9 +268,16 @@ export default class { // The image must be loaded to be drawn. exportImage.onload = () => { ctx.drawImage(exportImage, 0, 0, w, h); - body.removeChild(document.getElementById(img_id)); + const elImg = document.getElementById(img_id); + if (!elImg) { + throw new Error('Missing elImg'); + } + body.removeChild(elImg); // Let the user download the image. canvas.toBlob((blob) => { + if (!blob) { + throw new Error('Missing blob'); + } saveAs(blob, 'LIDAR_profile.png'); }); }; @@ -323,7 +333,7 @@ export default class { /** * Find the maximum value in am array of numbers - * @param {(Array.|undefined)} array of number + * @param {(Array)} array of number * @return {number} the maximum of input array */ arrayMax(array) { @@ -333,7 +343,7 @@ export default class { /** * Find the minimum value in am array of numbers - * @param {Array.|undefined} array of number + * @param {Array} array of number * @return {number} the minimum of input array */ arrayMin(array) { @@ -374,7 +384,7 @@ export default class { * @param {Function} sy d3.scalelinear y scale * @param {import("gmf/lidarprofile/Config.js").LidarprofileServerConfigClassifications} classification_colors * classification colors - * @return {LidarPoint} closestPoint the closest point to the clicked coordinates + * @return {?LidarPoint} closestPoint the closest point to the clicked coordinates */ getClosestPoint(points, xs, ys, tolerance, sx, sy, classification_colors) { const d = points; @@ -407,7 +417,7 @@ export default class { } } - let closestPoint; + let closestPoint = null; if (hP.length > 0) { const minDist = Math.min.apply(Math, distances); diff --git a/contribs/gmf/src/lidarprofile/component.js b/contribs/gmf/src/lidarprofile/component.js index b0389b06a30..197bb692c4a 100644 --- a/contribs/gmf/src/lidarprofile/component.js +++ b/contribs/gmf/src/lidarprofile/component.js @@ -15,7 +15,7 @@ module.value('gmfLidarprofileTemplateUrl', * @return {string} Template. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfLidarprofileTemplateUrl']; + const templateUrl = $attrs.gmfLidarprofileTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/lidarprofile'; }); @@ -83,9 +83,9 @@ class Controller { /** * The Openlayer LineStringt that defines the profile - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} */ - this.line; + this.line = null; /** * The profile active state diff --git a/contribs/gmf/src/lidarprofile/panelComponent.js b/contribs/gmf/src/lidarprofile/panelComponent.js index 35590df0f80..5a1d9b58489 100644 --- a/contribs/gmf/src/lidarprofile/panelComponent.js +++ b/contribs/gmf/src/lidarprofile/panelComponent.js @@ -29,7 +29,7 @@ module.value('gmfLidarprofilePanelTemplateUrl', * @return {string} Template. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfLidarprofilePanelTemplateUrl']; + const templateUrl = $attrs.gmfLidarprofilePanelTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/lidarprofilePanel'; }); @@ -132,15 +132,15 @@ class Controller { this.active = false; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ this.map = null; /** * The Openlayers LineString geometry of the profle - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} */ - this.line; + this.line = null; /** * State of the measure tool @@ -188,6 +188,9 @@ class Controller { * @private */ $onInit() { + if (!this.map) { + throw new Error('Missing map'); + } this.profile.init(this.profileConfig_, this.map); } @@ -227,6 +230,9 @@ class Controller { update_() { this.profile.clearBuffer(); if (this.line) { + if (!this.profileConfig_.serverConfig) { + throw new Error('Missing profileConfig_.serverConfig'); + } this.profile.setLine(this.line); this.profile.getProfileByLOD([], 0, true, this.profileConfig_.serverConfig.minLOD); } else { @@ -239,6 +245,7 @@ class Controller { */ clearAll() { this.line = null; + // @ts-ignore: OL issue this.profile.setLine(null); this.profile.cartoHighlight.setPosition(undefined); this.clearMeasure(); @@ -250,6 +257,9 @@ class Controller { * Activate the measure tool */ setMeasureActive() { + if (!this.profile.measure) { + throw new Error('Missing profile.measure'); + } this.measureActive = true; this.profile.measure.clearMeasure(); this.profile.measure.setMeasureActive(); @@ -260,6 +270,9 @@ class Controller { * Clear the current measure */ clearMeasure() { + if (!this.profile.measure) { + throw new Error('Missing profile.measure'); + } this.measureActive = false; this.profile.measure.clearMeasure(); } @@ -294,6 +307,9 @@ class Controller { * Selected point attribute */ getSetSelectedPointAttribute(opt_selectedOption) { + if (!this.profile.plot) { + throw new Error('Missing profile.plot'); + } if (opt_selectedOption !== undefined) { this.profileConfig_.clientConfig.pointAttributes.selectedOption = opt_selectedOption; this.profile.plot.changeStyle(opt_selectedOption.value); @@ -308,6 +324,9 @@ class Controller { * classification list */ getClassification() { + if (!this.profileConfig_.serverConfig) { + throw new Error('Missing profileConfig_.serverConfig'); + } return this.profileConfig_.serverConfig.classification_colors; } @@ -319,6 +338,12 @@ class Controller { * @param {number} key of the classification code */ setClassification(classification, key) { + if (!this.profile.plot) { + throw new Error('Missing profile.plot'); + } + if (!this.profileConfig_.serverConfig) { + throw new Error('Missing profileConfig_.serverConfig'); + } this.profileConfig_.serverConfig.classification_colors[key].visible = classification.visible; if (this.line) { this.profile.plot.setClassActive(this.profileConfig_.serverConfig.classification_colors, diff --git a/contribs/gmf/src/map/component.js b/contribs/gmf/src/map/component.js index ccbd2c8f138..0bf8527b658 100644 --- a/contribs/gmf/src/map/component.js +++ b/contribs/gmf/src/map/component.js @@ -74,37 +74,37 @@ function Controller(ngeoFeatureOverlayMgr, gmfPermalink, gmfSnapping) { // Scope properties /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {boolean|undefined} + * @type {?boolean} */ - this.manageResize; + this.manageResize = null; /** - * @type {boolean|undefined} + * @type {?boolean} */ - this.resizeTransition; + this.resizeTransition = null; // Injected properties /** - * @type {!import("ngeo/map/FeatureOverlayMgr.js").FeatureOverlayMgr} + * @type {import("ngeo/map/FeatureOverlayMgr.js").FeatureOverlayMgr} * @private */ this.ngeoFeatureOverlayMgr_ = ngeoFeatureOverlayMgr; /** - * @type {!import("gmf/permalink/Permalink.js").PermalinkService} + * @type {import("gmf/permalink/Permalink.js").PermalinkService} * @private */ this.gmfPermalink_ = gmfPermalink; /** - * @type {!import("gmf/editing/Snapping.js").EditingSnappingService} + * @type {import("gmf/editing/Snapping.js").EditingSnappingService} * @private */ this.gmfSnapping_ = gmfSnapping; @@ -115,6 +115,9 @@ function Controller(ngeoFeatureOverlayMgr, gmfPermalink, gmfSnapping) { * Called on initialization of the controller. */ Controller.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } this.ngeoFeatureOverlayMgr_.init(this.map); this.gmfPermalink_.setMap(this.map); this.gmfSnapping_.setMap(this.map); diff --git a/contribs/gmf/src/map/mousepositionComponent.js b/contribs/gmf/src/map/mousepositionComponent.js index d749b386a78..9d7dfa74bab 100644 --- a/contribs/gmf/src/map/mousepositionComponent.js +++ b/contribs/gmf/src/map/mousepositionComponent.js @@ -36,7 +36,7 @@ module.value('gmfMapMousepositionTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfMapMousepositionTemplateUrl']; + const templateUrl = $attrs.gmfMapMousepositionTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/map/mousepositionComponent'; }); @@ -98,19 +98,19 @@ module.component('gmfMouseposition', mapMousepositionComponent); */ function Controller($element, $filter, $scope, gettextCatalog) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {Array.} + * @type {Array} */ - this.projections; + this.projections = []; /** - * @type {!MousePositionProjection} + * @type {?MousePositionProjection} */ - this.projection; + this.projection = null; /** * @type {angular.IScope} @@ -162,14 +162,24 @@ Controller.prototype.$onInit = function() { * @private */ Controller.prototype.initOlControl_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } if (this.control_ !== null) { this.map.removeControl(this.control_); } // function that apply the filter. const formatFn = (coordinates) => { + if (!this.projection) { + throw new Error('Missing projection'); + } const filterAndArgs = this.projection.filter.split(':'); - const filter = this.$filter_(filterAndArgs.shift()); + const shiftedFilterAndArgs = filterAndArgs.shift(); + if (!shiftedFilterAndArgs) { + throw new Error('Missing shiftedFilterAndArgs'); + } + const filter = this.$filter_(shiftedFilterAndArgs); console.assert(typeof filter == 'function'); const args = filterAndArgs; args.unshift(coordinates); @@ -194,6 +204,9 @@ Controller.prototype.initOlControl_ = function() { * @param {MousePositionProjection} projection The new projection to use. */ Controller.prototype.setProjection = function(projection) { + if (!this.control_) { + throw new Error('Missing control'); + } this.control_.setProjection(projection.code); this.projection = projection; }; diff --git a/contribs/gmf/src/mobile/measure/areaComponent.js b/contribs/gmf/src/mobile/measure/areaComponent.js index cf4b5a69fd9..b7a0dd3c048 100644 --- a/contribs/gmf/src/mobile/measure/areaComponent.js +++ b/contribs/gmf/src/mobile/measure/areaComponent.js @@ -20,7 +20,7 @@ module.value('gmfMobileMeasureAreaTemplateUrl', * @return {string} The template url. */ (element, attrs) => { - const templateUrl = attrs['gmfMobileMeasureAreaTemplateurl']; + const templateUrl = attrs.gmfMobileMeasureAreaTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/measure/areaComponent'; }); @@ -70,9 +70,12 @@ function mobileMeasureAreaComponent(gmfMobileMeasureAreaTemplateUrl) { * @param {angular.IScope} scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} controller Controller. + * @param {angular.IController=} controller Controller. */ link: (scope, element, attrs, controller) => { + if (!controller) { + throw new Error('Missing controller'); + } controller.init(); } }; @@ -97,9 +100,9 @@ class Controller extends MeasueMobileBaseController { super($scope, $filter, gettextCatalog); /** - * @type {import("ngeo/interaction/MeasureAreaMobile.js").default} + * @type {?import("ngeo/interaction/MeasureAreaMobile.js").default} */ - this.measure; + this.measure = null; } /** @@ -118,6 +121,9 @@ class Controller extends MeasueMobileBaseController { * Add current sketch point to line measure */ addPoint() { + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.drawInteraction.addToDrawing(); } @@ -125,6 +131,9 @@ class Controller extends MeasueMobileBaseController { * Clear the sketch feature */ clear() { + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.drawInteraction.clearDrawing(); } @@ -132,6 +141,9 @@ class Controller extends MeasueMobileBaseController { * Finish line measure */ finish() { + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.drawInteraction.finishDrawing(); } diff --git a/contribs/gmf/src/mobile/measure/baseComponent.js b/contribs/gmf/src/mobile/measure/baseComponent.js index 9482511a27a..919c2ea2645 100644 --- a/contribs/gmf/src/mobile/measure/baseComponent.js +++ b/contribs/gmf/src/mobile/measure/baseComponent.js @@ -6,6 +6,7 @@ import olStyleFill from 'ol/style/Fill.js'; import olStyleRegularShape from 'ol/style/RegularShape.js'; import olStyleStroke from 'ol/style/Stroke.js'; import olStyleStyle from 'ol/style/Style.js'; +import MobileDraw from 'ngeo/interaction/MobileDraw'; /** @@ -50,23 +51,26 @@ export function MeasueMobileBaseController($scope, $filter, gettextCatalog) { this.gettextCatalog = gettextCatalog; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {boolean} */ - this.active; + this.active = false; this.scope.$watch(() => this.active, (newVal) => { + if (!this.measure) { + throw new Error('Missing measure'); + } this.measure.setActive(newVal); }); /** - * @type {number|undefined} + * @type {?number} */ - this.precision; + this.precision = null; /** * @type {import("ol/style/Style.js").StyleLike} @@ -93,14 +97,14 @@ export function MeasueMobileBaseController($scope, $filter, gettextCatalog) { }); /** - * @type {import("ngeo/interaction/Measure.js").default} + * @type {?import("ngeo/interaction/Measure.js").default} */ - this.measure; + this.measure = null; /** - * @type {import("ngeo/interaction/MobileDraw.js").default} + * @type {?import("ngeo/interaction/MobileDraw.js").default} */ - this.drawInteraction; + this.drawInteraction = null; /** * @type {boolean} @@ -123,14 +127,24 @@ export function MeasueMobileBaseController($scope, $filter, gettextCatalog) { * Initialise the controller. */ MeasueMobileBaseController.prototype.init = function() { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.measure) { + throw new Error('Missing measure'); + } + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.measure.setActive(this.active); interactionDecoration(this.measure); - this.drawInteraction = /** @type {import("ngeo/interaction/MobileDraw.js").default} */ ( - this.measure.getDrawInteraction()); - - const drawInteraction = this.drawInteraction; + const drawInteraction = this.measure.getDrawInteraction(); + if (!(drawInteraction instanceof MobileDraw)) { + throw new Error('Wrong drawInteraction'); + } + this.drawInteraction = drawInteraction; interactionDecoration(drawInteraction); Object.defineProperty(this, 'hasPoints', { diff --git a/contribs/gmf/src/mobile/measure/lengthComponent.js b/contribs/gmf/src/mobile/measure/lengthComponent.js index 1e593d3cf32..15e000ae5a8 100644 --- a/contribs/gmf/src/mobile/measure/lengthComponent.js +++ b/contribs/gmf/src/mobile/measure/lengthComponent.js @@ -20,7 +20,7 @@ module.value('gmfMobileMeasureLengthTemplateUrl', * @return {string} The template url. */ (element, attrs) => { - const templateUrl = attrs['gmfMobileMeasureLengthTemplateurl']; + const templateUrl = attrs.gmfMobileMeasureLengthTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/measure/lengthComponent'; }); @@ -70,9 +70,12 @@ function mobileMeasureLenthComponent(gmfMobileMeasureLengthTemplateUrl) { * @param {angular.IScope} scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} controller Controller. + * @param {angular.IController=} controller Controller. */ link: (scope, element, attrs, controller) => { + if (!controller) { + throw new Error('Missing controller'); + } controller.init(); } }; @@ -97,15 +100,18 @@ class Controller extends MeasueMobileBaseController { super($scope, $filter, gettextCatalog); /** - * @type {import("ngeo/interaction/MeasureLengthMobile.js").default} + * @type {?import("ngeo/interaction/MeasureLengthMobile.js").default} */ - this.measure; + this.measure = null; } /** * Initialise the controller. */ init() { + if (!this.precision) { + throw new Error('Missing precision'); + } this.measure = new ngeoInteractionMeasureLengthMobile( this.filter('ngeoUnitPrefix'), this.gettextCatalog, { precision: this.precision, @@ -120,6 +126,9 @@ class Controller extends MeasueMobileBaseController { * Add current sketch point to line measure */ addPoint() { + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.drawInteraction.addToDrawing(); } @@ -127,6 +136,9 @@ class Controller extends MeasueMobileBaseController { * Clear the sketch feature */ clear() { + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.drawInteraction.clearDrawing(); } @@ -134,6 +146,9 @@ class Controller extends MeasueMobileBaseController { * Finish line measure */ finish() { + if (!this.drawInteraction) { + throw new Error('Missing drawInteraction'); + } this.drawInteraction.finishDrawing(); } diff --git a/contribs/gmf/src/mobile/measure/pointComponent.js b/contribs/gmf/src/mobile/measure/pointComponent.js index a708cfe6310..c3029a20b08 100644 --- a/contribs/gmf/src/mobile/measure/pointComponent.js +++ b/contribs/gmf/src/mobile/measure/pointComponent.js @@ -8,6 +8,7 @@ import olStyleFill from 'ol/style/Fill.js'; import olStyleRegularShape from 'ol/style/RegularShape.js'; import olStyleStroke from 'ol/style/Stroke.js'; import olStyleStyle from 'ol/style/Style.js'; +import MobileDraw from 'ngeo/interaction/MobileDraw'; /** @@ -27,7 +28,7 @@ module.value('gmfMobileMeasurePointTemplateUrl', * @return {string} The template url. */ (element, attrs) => { - const templateUrl = attrs['gmfMobileMeasurePointTemplateurl']; + const templateUrl = attrs.gmfMobileMeasurePointTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/measure/pointComponent'; }); @@ -93,9 +94,12 @@ function mobileMeasurePointComponent(gmfMobileMeasurePointTemplateUrl) { * @param {!angular.IScope} scope Scope. * @param {!JQuery} element Element. * @param {!angular.IAttributes} attrs Attributes. - * @param {!angular.IController} controller Controller. + * @param {!angular.IController=} controller Controller. */ link: (scope, element, attrs, controller) => { + if (!controller) { + throw new Error('Missing controller'); + } controller.init(); } }; @@ -144,21 +148,26 @@ export function MobileMeasurePointController(gettextCatalog, $scope, $filter, gm this.$filter_ = $filter; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {boolean} */ - this.active; + this.active = false; + + this.getCoordinateDecimalsFn = () => 0; $scope.$watch(() => this.active, (newVal) => { + if (!this.measure) { + throw new Error('Missing measure'); + } this.measure.setActive(newVal); this.handleMeasureActiveChange_(); }); - const coordinateDecimalsFn = this['getCoordinateDecimalsFn']; + const coordinateDecimalsFn = this.getCoordinateDecimalsFn; /** * @type {number} @@ -167,15 +176,15 @@ export function MobileMeasurePointController(gettextCatalog, $scope, $filter, gm this.coordinateDecimals = coordinateDecimalsFn ? coordinateDecimalsFn() : 0; /** - * @type {!Array} + * @type {Array} * @private */ - this.layersConfig; + this.layersConfig = []; /** * @type {import("ol/style/Style.js").StyleLike} */ - this.sketchStyle; + this.sketchStyle = []; if (this.sketchStyle === undefined) { this.sketchStyle = new olStyleStyle({ @@ -201,19 +210,21 @@ export function MobileMeasurePointController(gettextCatalog, $scope, $filter, gm } /** - * @type {string} + * @type {?string} */ - this.format; + this.format = null; /** - * @type {import("ngeo/interaction/MeasurePointMobile.js").default} + * @type {?import("ngeo/interaction/MeasurePointMobile.js").default} */ - this.measure; + this.measure = null; /** - * @type {import("ngeo/interaction/MobileDraw.js").default} + * @type {?import("ngeo/interaction/MobileDraw.js").default} */ - this.drawInteraction; + this.drawInteraction = null; + + this.getLayersConfigFn = () => []; /** * The key for map view 'propertychange' event. @@ -238,14 +249,20 @@ MobileMeasurePointController.prototype.init = function() { ); this.measure.setActive(this.active); interactionDecoration(this.measure); - this.drawInteraction = /** @type {import("ngeo/interaction/MobileDraw.js").default} */ ( - this.measure.getDrawInteraction()); + const drawInteraction = this.measure.getDrawInteraction(); + if (!(drawInteraction instanceof MobileDraw)) { + throw new Error('Wrong drawInteraction'); + } + this.drawInteraction = drawInteraction; interactionDecoration(this.drawInteraction); - const layersConfig = this['getLayersConfigFn'](); + const layersConfig = this.getLayersConfigFn(); console.assert(Array.isArray(layersConfig)); this.layersConfig = layersConfig; + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(this.measure); }; @@ -276,6 +293,12 @@ MobileMeasurePointController.prototype.translate = function(str) { * @hidden */ MobileMeasurePointController.prototype.handleMeasureActiveChange_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.measure) { + throw new Error('Missing measure'); + } if (this.measure.getActive()) { const view = this.map.getView(); this.mapViewPropertyChangeEventKey_ = olEvents.listen( @@ -299,12 +322,20 @@ MobileMeasurePointController.prototype.handleMeasureActiveChange_ = function() { * @hidden */ MobileMeasurePointController.prototype.getMeasure_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } const center = this.map.getView().getCenter(); - console.assert(Array.isArray(center)); + if (!Array.isArray(center)) { + throw new Error('Wrong center'); + } const params = { 'layers': this.layersConfig.map(config => config.name).join(',') }; this.gmfRaster_.getRaster(center, params).then((object) => { + if (!this.measure) { + throw new Error('Missing measure'); + } const el = this.measure.getTooltipElement(); const ctn = document.createElement('div'); const className = 'gmf-mobile-measure-point'; diff --git a/contribs/gmf/src/mobile/navigation/component.js b/contribs/gmf/src/mobile/navigation/component.js index 1ee3e7114f9..50b2ccb7d2d 100644 --- a/contribs/gmf/src/mobile/navigation/component.js +++ b/contribs/gmf/src/mobile/navigation/component.js @@ -70,9 +70,12 @@ function mobileNavigationComponent() { * @param {angular.IScope} scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} navCtrl Controller. + * @param {angular.IController=} navCtrl Controller. */ link: (scope, element, attrs, navCtrl) => { + if (!navCtrl) { + throw new Error('Missing navCtrl'); + } navCtrl.init(element); } }; @@ -93,28 +96,28 @@ function Controller() { /** * Stack of slid-in items. * @private - * @type {Array.} + * @type {Array} */ this.slid_ = []; /** * Currently active sliding box. * @private - * @type {JQuery} + * @type {?JQuery} */ this.active_ = null; /** * The navigation header. * @private - * @type {JQuery} + * @type {?JQuery} */ this.header_ = null; /** * The back button in the navigation header. * @private - * @type {JQuery} + * @type {?JQuery} */ this.backButton_ = null; @@ -143,6 +146,7 @@ Controller.prototype.init = function(element) { * @param {JQuery.ClickEvent} evt The event */ const onClick = (evt) => { + const slideOut = $(evt.currentTarget).parents(`.${CLASS_NAMES.SLIDE}`); console.assert(slideOut.length === 1); @@ -153,7 +157,11 @@ Controller.prototype.init = function(element) { slideOut.addClass(CLASS_NAMES.SLIDE_OUT).removeClass(CLASS_NAMES.ACTIVE); // element to slide in - const slideIn = $($(evt.currentTarget).attr('data-target')); + const datatarget = $(evt.currentTarget).attr('data-target'); + if (!datatarget) { + throw new Error('Missing datatarget'); + } + const slideIn = $(datatarget); console.assert(slideIn.length === 1); // slide the "new" element in @@ -179,6 +187,12 @@ Controller.prototype.init = function(element) { * @private */ Controller.prototype.updateNavigationHeader_ = function(active, back) { + if (!this.header_) { + throw new Error('Missing header'); + } + if (!this.backButton_) { + throw new Error('Missing backButton'); + } this.header_.toggleClass(CLASS_NAMES.BACK, back); // remove any inactive nav @@ -224,6 +238,9 @@ Controller.prototype.updateNavigationHeader_ = function(active, back) { * @private */ Controller.prototype.back_ = function() { + if (!this.active_) { + throw new Error('Missing active'); + } if (this.slid_.length <= 0) { return; } @@ -233,6 +250,9 @@ Controller.prototype.back_ = function() { // get the previously active item const slideBack = this.slid_.pop(); + if (!slideBack) { + throw new Error('Missing slideBack'); + } // slide previous item to the right slideBack.addClass(CLASS_NAMES.ACTIVE).removeClass(CLASS_NAMES.SLIDE_OUT); @@ -286,10 +306,13 @@ function mobileNavigationBackComponent() { * @param {angular.IScope} scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} navCtrl Controller. + * @param {angular.IController=} navCtrl Controller. */ link: (scope, element, attrs, navCtrl) => { - scope.$watch(attrs['gmfMobileNavBack'], (newVal, oldVal) => { + scope.$watch(attrs.gmfMobileNavBack, (newVal, oldVal) => { + if (!navCtrl) { + throw new Error('Missing navCtrl'); + } if (newVal === true) { navCtrl.backIfActive(element[0]); } @@ -329,10 +352,13 @@ function mobileNavigationBackOnClickComponent() { * @param {angular.IScope} scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} navCtrl Controller. + * @param {angular.IController=} navCtrl Controller. */ link: (scope, element, attrs, navCtrl) => { element.on('click', () => { + if (!navCtrl) { + throw new Error('Missing navCtrl'); + } navCtrl.backIfActive(element[0]); }); } diff --git a/contribs/gmf/src/objectediting/Manager.js b/contribs/gmf/src/objectediting/Manager.js index c6fb0f5df8c..b19c730fcd7 100644 --- a/contribs/gmf/src/objectediting/Manager.js +++ b/contribs/gmf/src/objectediting/Manager.js @@ -149,10 +149,13 @@ ObjecteditingManagerService.prototype.handleGetFeatures_ = function(key, value, } else { const featureProperties = {}; featureProperties[key] = value; - featureProperties['geometry'] = null; + featureProperties.geometry = null; feature = new olFeature(featureProperties); } + if (!this.getFeatureDefered_) { + throw new Error('Missing getFeatureDefered'); + } this.getFeatureDefered_.resolve(feature); }; diff --git a/contribs/gmf/src/objectediting/Query.js b/contribs/gmf/src/objectediting/Query.js index c29e8493dcd..69829495652 100644 --- a/contribs/gmf/src/objectediting/Query.js +++ b/contribs/gmf/src/objectediting/Query.js @@ -58,6 +58,9 @@ ObjectEditingQuery.prototype.getQueryableLayersInfo = function() { if (!themes) { return; } + if (!this.getQueryableLayerNodesDefered_) { + throw new Error('Missing getQueryableLayerNodesDefered'); + } // Get all queryable nodes const allQueryableLayersInfo = diff --git a/contribs/gmf/src/objectediting/component.js b/contribs/gmf/src/objectediting/component.js index 53461980324..264adf86538 100644 --- a/contribs/gmf/src/objectediting/component.js +++ b/contribs/gmf/src/objectediting/component.js @@ -85,7 +85,7 @@ module.value('gmfObjecteditingTemplateUrl', * @return {string} Template URL. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfObjecteditingTemplateurl']; + const templateUrl = $attrs.gmfObjecteditingTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/objectediting'; } @@ -177,32 +177,32 @@ function Controller($scope, $timeout, gettextCatalog, /** * @type {boolean} */ - this.active; + this.active = false; /** - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ - this.feature; + this.feature = null; /** - * @type {string} + * @type {?string} */ - this.geomType; + this.geomType = null; /** - * @type {number} + * @type {?number} */ - this.layerNodeId; + this.layerNodeId = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {import("ol/Collection.js").default.} + * @type {?import("ol/Collection.js").default.} */ - this.sketchFeatures; + this.sketchFeatures = null; // == Injected properties == @@ -238,14 +238,14 @@ function Controller($scope, $timeout, gettextCatalog, this.gmfObjectEditingQuery_ = gmfObjectEditingQuery; /** - * @type {Array.} + * @type {Array} */ - this.queryableLayersInfo; + this.queryableLayersInfo = []; /** - * @type {import('gmf/objectediting/toolsComponent.js').ObjectEditingQueryableLayerInfo} + * @type {?import('gmf/objectediting/toolsComponent.js').ObjectEditingQueryableLayerInfo} */ - this.selectedQueryableLayerInfo; + this.selectedQueryableLayerInfo = null; /** * Whether to show or hide the queryable list of layers. It is shown only @@ -268,7 +268,7 @@ function Controller($scope, $timeout, gettextCatalog, /** * @type {boolean} */ - this.featureHasGeom; + this.featureHasGeom = false; /** * @type {!import("ngeo/map/LayerHelper.js").LayerHelper} @@ -315,7 +315,7 @@ function Controller($scope, $timeout, gettextCatalog, this.editableWMSLayer_ = null; /** - * @type {!jsts.io.OL3Parser} + * @type {jsts.io.OL3Parser} * @private */ this.jstsOL3Parser_ = new OL3Parser(); @@ -323,19 +323,19 @@ function Controller($scope, $timeout, gettextCatalog, /** * The state of the feature determines whether the next 'save' request * should be an 'insert' or 'update' one. - * @type {string|undefined} + * @type {?string} * @private */ - this.state_; + this.state_ = null; /** - * @type {!Array.} + * @type {Array} * @private */ this.geometryChanges_ = []; /** - * @type {!StylesObject} + * @type {StylesObject} * @private */ this.defaultStyles_ = {}; @@ -444,6 +444,9 @@ Controller.prototype.$onInit = function() { // leaf nodes are created and they are the ones we're looking for here. this.timeout_(() => { if (value) { + if (!this.gmfTreeManager_.rootCtrl) { + throw new Error('Missing gmfTreeManager_.rootCtrl'); + } this.unregisterAllTreeCtrl_(); this.gmfTreeManager_.rootCtrl.traverseDepthFirst( this.registerTreeCtrl_.bind(this) @@ -453,6 +456,9 @@ Controller.prototype.$onInit = function() { } ); + if (!this.feature) { + throw new Error('Missing feature'); + } const geometry = this.feature.getGeometry(); this.state_ = geometry ? ObjecteditingState.UPDATE : ObjecteditingState.INSERT; @@ -508,6 +514,12 @@ Controller.prototype.delete = function() { if (confirm(msg)) { this.dirty = false; this.pending = true; + if (!this.layerNodeId) { + throw new Error('Missing layerNodeId'); + } + if (!this.feature) { + throw new Error('Missing feature'); + } this.gmfEditFeature_.deleteFeature( this.layerNodeId, @@ -524,6 +536,12 @@ Controller.prototype.delete = function() { * Save the current modifications. */ Controller.prototype.save = function() { + if (!this.feature) { + throw new Error('Missing feature'); + } + if (!this.layerNodeId) { + throw new Error('Missing layerNodeId'); + } this.pending = true; @@ -562,6 +580,9 @@ Controller.prototype.save = function() { * Undo the latest modifications. */ Controller.prototype.undo = function() { + if (!this.feature) { + throw new Error('Missing feature'); + } if (this.geometryChanges_.length <= 1) { return; @@ -570,8 +591,10 @@ Controller.prototype.undo = function() { this.skipGeometryChange_ = true; this.geometryChanges_.pop(); - const clone = cloneGeometry( - this.geometryChanges_[this.geometryChanges_.length - 1]); + const clone = cloneGeometry(this.geometryChanges_[this.geometryChanges_.length - 1]); + if (!clone) { + throw new Error('Missing clone'); + } this.feature.setGeometry(clone); @@ -596,7 +619,10 @@ Controller.prototype.isStateInsert = function() { * @private */ Controller.prototype.handleDeleteFeature_ = function(resp) { - this.feature.setGeometry(null); + if (!this.feature) { + throw new Error('Missing feature'); + } + this.feature.setGeometry(undefined); this.resetGeometryChanges_(); this.state_ = ObjecteditingState.INSERT; this.pending = false; @@ -610,6 +636,10 @@ Controller.prototype.handleDeleteFeature_ = function(resp) { * @private */ Controller.prototype.handleEditFeature_ = function(resp) { + if (!this.feature) { + throw new Error('Missing feature'); + } + // (1) Update the id const features = new olFormatGeoJSON().readFeatures(resp.data); if (features.length) { @@ -648,6 +678,9 @@ Controller.prototype.initializeInteractions_ = function() { */ Controller.prototype.registerInteractions_ = function() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(interaction); }); }; @@ -659,6 +692,9 @@ Controller.prototype.registerInteractions_ = function() { */ Controller.prototype.unregisterInteractions_ = function() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.removeInteraction(interaction); }); }; @@ -670,6 +706,12 @@ Controller.prototype.unregisterInteractions_ = function() { * @private */ Controller.prototype.toggle_ = function(active) { + if (!this.feature) { + throw new Error('Missing feature'); + } + if (!this.sketchFeatures) { + throw new Error('Missing sketchFeatures'); + } const keys = this.listenerKeys_; const uid = `${NAMESPACE}-${olUtilGetUid(this)}`; @@ -750,8 +792,13 @@ Controller.prototype.toggle_ = function(active) { * @private */ Controller.prototype.undoAllChanges_ = function() { - const clone = cloneGeometry( - this.geometryChanges_[0]); + if (!this.feature) { + throw new Error('Missing feature'); + } + const clone = cloneGeometry(this.geometryChanges_[0]); + if (!clone) { + throw new Error('Missing clone'); + } this.feature.setGeometry(clone); this.resetGeometryChanges_(); @@ -771,6 +818,9 @@ Controller.prototype.resetGeometryChanges_ = function() { this.geometryChanges_.length = 0; } if (this.geometryChanges_.length === 0) { + if (!this.feature) { + throw new Error('Missing feature'); + } const geometry = this.feature.getGeometry(); const clone = cloneGeometry(geometry); this.geometryChanges_.push(clone); @@ -789,10 +839,17 @@ Controller.prototype.resetGeometryChanges_ = function() { * @private */ Controller.prototype.handleModifyInteractionModifyEnd_ = function(evt) { + if (!this.feature) { + throw new Error('Missing feature'); + } let geometry = this.feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } if (geometry.getType() === 'MultiPolygon') { const jstsGeom = this.jstsOL3Parser_.read(geometry); + // @ts-ignore: jsts issue? const jstsBuffered = jstsGeom.buffer(0, undefined, undefined); geometry = toMulti(this.jstsOL3Parser_.write(jstsBuffered)); this.skipGeometryChange_ = true; @@ -910,6 +967,9 @@ Controller.prototype.initializeStyles_ = function( * @private */ Controller.prototype.setFeatureStyle_ = function() { + if (!this.feature) { + throw new Error('Missing feature'); + } const geometry = this.feature.getGeometry(); if (geometry) { const geomType = geometry.getType(); @@ -1018,6 +1078,12 @@ Controller.prototype.handleWindowBeforeUnload_ = function(e) { */ Controller.prototype.handleSketchFeaturesAdd_ = function(evt) { if (evt instanceof CollectionEvent) { + if (!this.feature) { + throw new Error('Missing feature'); + } + if (!this.sketchFeatures) { + throw new Error('Missing sketchFeatures'); + } const sketchFeature = evt.element; const sketchGeom = sketchFeature.getGeometry(); @@ -1063,6 +1129,9 @@ Controller.prototype.handleSketchFeaturesAdd_ = function(evt) { * @private */ Controller.prototype.handleFeatureGeometryChange_ = function() { + if (!this.feature) { + throw new Error('Missing feature'); + } const geom = this.feature.getGeometry(); this.timeout_(() => { this.featureHasGeom = !isEmpty(geom); diff --git a/contribs/gmf/src/objectediting/coordinate.js b/contribs/gmf/src/objectediting/coordinate.js index ccbb92a3b88..d6c109f7ad8 100644 --- a/contribs/gmf/src/objectediting/coordinate.js +++ b/contribs/gmf/src/objectediting/coordinate.js @@ -13,9 +13,10 @@ */ export function coordinatesToXY0(coordinates) { if (coordinates.length > 2) { - const coord = /** @type{import("ol/coordinate.js").Coordinate} */(coordinates); + const coord = coordinates; return [coord[0], coord[1]]; } + return coordinates; } diff --git a/contribs/gmf/src/objectediting/getWMSFeatureComponent.js b/contribs/gmf/src/objectediting/getWMSFeatureComponent.js index 426bbc65722..5076ed7da84 100644 --- a/contribs/gmf/src/objectediting/getWMSFeatureComponent.js +++ b/contribs/gmf/src/objectediting/getWMSFeatureComponent.js @@ -72,7 +72,7 @@ function Controller($scope, gmfObjectEditingQuery) { /** * @type {boolean} */ - this.active; + this.active = false; $scope.$watch( () => this.active, @@ -80,19 +80,19 @@ function Controller($scope, gmfObjectEditingQuery) { ); /** - * @type {import("ol/Collection.js").default} + * @type {?import("ol/Collection.js").default} */ - this.features; + this.features = null; /** - * @type {import('gmf/objectediting/toolsComponent.js').ObjectEditingQueryableLayerInfo} + * @type {?import('gmf/objectediting/toolsComponent.js').ObjectEditingQueryableLayerInfo} */ - this.layerInfo; + this.layerInfo = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; // Injected properties @@ -111,6 +111,9 @@ function Controller($scope, gmfObjectEditingQuery) { * @private */ Controller.prototype.handleActiveChange_ = function(active) { + if (!this.map) { + throw new Error('Missing map'); + } if (active) { olEvents.listen( this.map, @@ -135,12 +138,21 @@ Controller.prototype.handleActiveChange_ = function(active) { */ Controller.prototype.handleMapClick_ = function(evt) { if (evt instanceof MapBrowserEvent) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.layerInfo) { + throw new Error('Missing layerInfo'); + } this.gmfObjectEditingQuery_.getFeatureInfo( this.layerInfo, evt.coordinate, this.map ).then((feature) => { if (feature) { + if (!this.features) { + throw new Error('Missing features'); + } this.features.push(feature); } }); diff --git a/contribs/gmf/src/objectediting/toolsComponent.js b/contribs/gmf/src/objectediting/toolsComponent.js index cb7321ef645..d6d0159718b 100644 --- a/contribs/gmf/src/objectediting/toolsComponent.js +++ b/contribs/gmf/src/objectediting/toolsComponent.js @@ -162,52 +162,52 @@ function Controller($injector, $scope, ngeoToolActivateMgr) { /** * @type {boolean} */ - this.active; + this.active = false; /** * @type {boolean} */ - this.copyFromActive; + this.copyFromActive = false; /** * @type {boolean} */ - this.deleteFromActive; + this.deleteFromActive = false; /** - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ - this.feature; + this.feature = null; /** - * @type {string} + * @type {?string} */ - this.geomType; + this.geomType = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {ObjectEditingQueryableLayerInfo} + * @type {?ObjectEditingQueryableLayerInfo} */ - this.queryableLayerInfo; + this.queryableLayerInfo = null; /** - * @type {string} + * @type {?string} */ - this.process; + this.process = null; /** * @type {boolean} */ - this.requiresLayer; + this.requiresLayer = false; /** - * @type {import("ol/Collection.js").default.} + * @type {?import("ol/Collection.js").default.} */ - this.sketchFeatures; + this.sketchFeatures = null; // == Injected properties == diff --git a/contribs/gmf/src/permalink/Permalink.js b/contribs/gmf/src/permalink/Permalink.js index 4a73e2e159e..5756a3f6fb3 100644 --- a/contribs/gmf/src/permalink/Permalink.js +++ b/contribs/gmf/src/permalink/Permalink.js @@ -470,11 +470,12 @@ export function PermalinkService( * @type {import("ol/style/Style.js").StyleLike} * @private */ - this.crosshairStyle_; + this.crosshairStyle_ = []; if (gmfPermalinkOptions.crosshairStyle !== undefined) { this.crosshairStyle_ = gmfPermalinkOptions.crosshairStyle; } else { + // @ts-ignore: OL issue this.crosshairStyle_ = [new olStyleStyle({ image: new olStyleIcon({ // @ts-ignore: webpack @@ -512,7 +513,13 @@ export function PermalinkService( 'strokeWidth': ngeoFormatFeatureProperties.STROKE }, defaultValues: { - 'name': feature => gettextCatalog.getString(feature.getGeometry().getType()), + 'name': feature => { + const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + return gettextCatalog.getString(geometry.getType()); + }, 'fillOpacity': () => 0.5, 'showLabel': () => false, 'showMeasure': () => false @@ -585,6 +592,9 @@ export function PermalinkService( if (this.featureHelper_) { this.rootScope_.$on('$localeChangeSuccess', () => { features.forEach((feature) => { + if (!this.featureHelper_) { + throw new Error('Missing featureHelper'); + } this.featureHelper_.setStyle(feature); }); }); @@ -607,6 +617,9 @@ export function PermalinkService( if (this.ngeoQuerent_ && this.gmfExternalDataSourcesManager_) { // First, load the external data sources that are defined in the url this.initExternalDataSources_().then(() => { + if (!this.gmfExternalDataSourcesManager_) { + throw new Error('Missing gmfExternalDataSourcesManager'); + } // Then, listen to the changes made to the external data sources to // update the url accordingly. olEvents.listen( @@ -656,10 +669,13 @@ export function PermalinkService( * @return {?import("ol/coordinate.js").Coordinate} The coordinate for the map view center. */ PermalinkService.prototype.getMapCenter = function() { + if (!this.map_) { + throw new Error('Missing map'); + } const x = this.ngeoStateManager_.getInitialNumberValue(PermalinkParam.MAP_X); const y = this.ngeoStateManager_.getInitialNumberValue(PermalinkParam.MAP_Y); - if (!isNaN(x) && !isNaN(y)) { + if (x !== undefined && y !== undefined && !isNaN(x) && !isNaN(y)) { const center = [x, y]; if (this.sourceProjections_ !== null && this.ngeoAutoProjection_) { const targetProjection = this.map_.getView().getProjection(); @@ -682,7 +698,7 @@ PermalinkService.prototype.getMapCenter = function() { */ PermalinkService.prototype.getMapZoom = function() { const zoom = this.ngeoStateManager_.getInitialNumberValue(PermalinkParam.MAP_Z); - return isNaN(zoom) ? undefined : zoom; + return zoom !== undefined && isNaN(zoom) ? undefined : zoom; }; @@ -705,21 +721,28 @@ PermalinkService.prototype.getMapCrosshair = function() { * @param {?import("ol/coordinate.js").Coordinate=} opt_center Optional center coordinate. */ PermalinkService.prototype.setMapCrosshair = function(opt_center) { + if (!this.map_) { + throw new Error('Missing map'); + } + if (!this.featureOverlay_) { + throw new Error('Missing featureOverlay'); + } let crosshairCoordinate; if (opt_center) { crosshairCoordinate = opt_center; } else { crosshairCoordinate = this.map_.getView().getCenter(); } - console.assert(Array.isArray(crosshairCoordinate)); + if (!Array.isArray(crosshairCoordinate)) { + throw new Error('Wrong crosshairCoordinate'); + } // remove existing crosshair first if (this.crosshairFeature_) { this.featureOverlay_.removeFeature(this.crosshairFeature_); } // set new crosshair - this.crosshairFeature_ = new olFeature( - new olGeomPoint(crosshairCoordinate)); + this.crosshairFeature_ = new olFeature(new olGeomPoint(crosshairCoordinate)); this.crosshairFeature_.setStyle(this.crosshairStyle_); // add to overlay @@ -745,6 +768,9 @@ PermalinkService.prototype.getMapTooltip = function() { * @param {?import("ol/coordinate.js").Coordinate=} opt_center Optional center coordinate. */ PermalinkService.prototype.setMapTooltip = function(tooltipText, opt_center) { + if (!this.map_) { + throw new Error('Missing map'); + } let tooltipPosition; if (opt_center) { tooltipPosition = opt_center; @@ -788,7 +814,7 @@ PermalinkService.prototype.getFeatures = function() { /** - * @param {!Object.} dimensions The global dimensions object. + * @param {Object} dimensions The global dimensions object. */ PermalinkService.prototype.setDimensions = function(dimensions) { // apply initial state @@ -796,7 +822,9 @@ PermalinkService.prototype.setDimensions = function(dimensions) { for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = this.ngeoLocation_.getParam(key); - console.assert(value); + if (!value) { + throw new Error('Missing value'); + } dimensions[key.slice(ParamPrefix.DIMENSIONS.length)] = value; } @@ -862,15 +890,20 @@ PermalinkService.prototype.registerMap_ = function(map, oeFeature) { const geom = typeof oeFeature !== 'undefined' && oeFeature !== null ? oeFeature.getGeometry() : undefined; if (geom) { const size = map.getSize(); - console.assert(size); + if (!size) { + throw new Error('Missing size'); + } let maxZoom; if (geom instanceof olGeomPoint || geom instanceof olGeomMultiPoint) { maxZoom = this.pointRecenterZoom_; } - view.fit(geom.getExtent(), { + const options = { size, - maxZoom - }); + }; + if (maxZoom) { + options.maxZoom = maxZoom; + } + view.fit(geom.getExtent(), options); } else { const enabled3d = this.ngeoStateManager_.getInitialBooleanValue(Permalink3dParam.ENABLED); if (!enabled3d) { @@ -885,7 +918,6 @@ PermalinkService.prototype.registerMap_ = function(map, oeFeature) { } } - // (2) Listen to any property changes within the view and apply them to // the permalink service this.mapViewPropertyChangeEventKey_ = olEvents.listen( @@ -893,8 +925,11 @@ PermalinkService.prototype.registerMap_ = function(map, oeFeature) { 'propertychange', this.ngeoDebounce_(() => { const center = view.getCenter(); + if (!center) { + throw new Error('Missing center'); + } const zoom = view.getZoom(); - /** @type {!Object.} */ + /** @type {Object} */ const object = {}; object[PermalinkParam.MAP_X] = `${Math.round(center[0])}`; object[PermalinkParam.MAP_Y] = `${Math.round(center[1])}`; @@ -927,8 +962,9 @@ PermalinkService.prototype.registerMap_ = function(map, oeFeature) { * @private */ PermalinkService.prototype.unregisterMap_ = function() { - console.assert( - this.mapViewPropertyChangeEventKey_, 'Key should be thruthy'); + if (!this.mapViewPropertyChangeEventKey_) { + throw new Error('Missing mapViewPropertyChangeEventKey'); + } olEvents.unlistenByKey(this.mapViewPropertyChangeEventKey_); this.mapViewPropertyChangeEventKey_ = null; }; @@ -968,6 +1004,9 @@ PermalinkService.prototype.handleBackgroundLayerManagerChange_ = function() { // get layer label, i.e its name const layer = this.ngeoBackgroundLayerMgr_.get(this.map_); + if (!layer) { + throw new Error('Missing layer'); + } const layerName = layer.get('label'); console.assert(typeof layerName == 'string'); @@ -990,6 +1029,9 @@ PermalinkService.prototype.refreshFirstLevelGroups = function() { if (!this.gmfTreeManager_) { return; } + if (!this.gmfTreeManager_.rootCtrl) { + throw new Error('Missing gmfTreeManager_.rootCtrl'); + } // Get first-level-groups order const groupNodes = this.gmfTreeManager_.rootCtrl.node.children; const orderedNames = groupNodes.map(node => node.name); @@ -1019,8 +1061,12 @@ PermalinkService.prototype.themeInUrl_ = function(pathElements) { * @private */ PermalinkService.prototype.setThemeInUrl_ = function(themeName) { + const path = this.ngeoLocation_.getPath(); + if (!path) { + throw new Error('Missing path'); + } if (themeName) { - const pathElements = this.ngeoLocation_.getPath().split('/'); + const pathElements = path.split('/'); console.assert(pathElements.length > 1); if (pathElements[pathElements.length - 1] === '') { // case where the path is just "/" @@ -1042,9 +1088,12 @@ PermalinkService.prototype.setThemeInUrl_ = function(themeName) { * @return {?string} default theme name. */ PermalinkService.prototype.defaultThemeName = function() { - + const path = this.ngeoLocation_.getPath(); + if (!path) { + throw new Error('Missing path'); + } // check if we have a theme in url - const pathElements = this.ngeoLocation_.getPath().split('/'); + const pathElements = path.split('/'); if (this.themeInUrl_(pathElements)) { return decodeURI(pathElements[pathElements.length - 1]); } @@ -1101,7 +1150,9 @@ PermalinkService.prototype.initLayers_ = function() { } this.gmfThemes_.getThemesObject().then((themes) => { const themeName = this.defaultThemeName(); - console.assert(themeName !== null); + if (themeName === null) { + throw new Error('Missing themeName'); + } if (this.gmfThemeManager_) { this.gmfThemeManager_.setThemeName(this.gmfThemeManager_.modeFlush ? themeName : ''); @@ -1388,7 +1439,12 @@ PermalinkService.prototype.createFilterGroup_ = function(prefix, paramKeys) { */ PermalinkService.prototype.initExternalDataSources_ = function() { - + if (!this.ngeoQuerent_) { + throw new Error('Missing ngeoQuerent'); + } + if (!this.gmfExternalDataSourcesManager_) { + throw new Error('Missing gmfExternalDataSourcesManager'); + } const ngeoQuerent = this.ngeoQuerent_; const gmfExtDSManager = this.gmfExternalDataSourcesManager_; @@ -1480,7 +1536,7 @@ PermalinkService.prototype.initExternalDataSources_ = function() { if (response.serviceType === Type.WMS) { for (const layerName of response.groupLayerNames) { const layerCap = ngeoQuerent.wmsFindLayerCapability( - response.capabilities['Capability']['Layer']['Layer'], + response.capabilities.Capability.Layer.Layer, layerName ); if (layerCap) { @@ -1500,7 +1556,7 @@ PermalinkService.prototype.initExternalDataSources_ = function() { // create the data source for (const layerName of response.groupLayerNames) { const layerCap = ngeoQuerent.wmtsFindLayerCapability( - response.capabilities['Contents']['Layer'], + response.capabilities.Contents.Layer, layerName ); if (layerCap) { @@ -1623,6 +1679,9 @@ PermalinkService.prototype.setExternalDataSourcesState_ = function() { } this.setExternalDataSourcesStatePromise_ = this.$timeout_(() => { + if (!this.gmfExternalDataSourcesManager_) { + throw new Error('Missing gmfExternalDataSourcesManager'); + } const names = []; const urls = []; diff --git a/contribs/gmf/src/permalink/shareComponent.js b/contribs/gmf/src/permalink/shareComponent.js index d630b26199f..b10af6d83f2 100644 --- a/contribs/gmf/src/permalink/shareComponent.js +++ b/contribs/gmf/src/permalink/shareComponent.js @@ -26,7 +26,7 @@ module.value('gmfPermalinkShareTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfPermalinkShareTemplateUrl']; + const templateUrl = $attrs.gmfPermalinkShareTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/permalink/shareComponent'; }); @@ -117,22 +117,22 @@ class ShareComponentController { /** * @type {boolean} */ - this.enableEmail; + this.enableEmail = false; /** * @type {string} */ - this.shortLink; + this.shortLink = ''; /** * @type {string} */ - this.email; + this.email = ''; /** * @type {string} */ - this.message; + this.message = ''; /** * @type {string} @@ -140,11 +140,15 @@ class ShareComponentController { */ this.permalink_ = this.ngeoLocation_.getUriString(); + const path = ngeoLocation.getPath(); + if (!path) { + throw new Error('Missing path'); + } /** * @type {boolean} */ this.showLengthWarning = this.permalink_.length > URL_MAX_LEN || - ngeoLocation.getPath().length > URL_PATH_MAX_LEN; + path.length > URL_PATH_MAX_LEN; /** * @type {boolean} diff --git a/contribs/gmf/src/print/component.js b/contribs/gmf/src/print/component.js index d0a238f8991..18e99904acb 100644 --- a/contribs/gmf/src/print/component.js +++ b/contribs/gmf/src/print/component.js @@ -93,7 +93,7 @@ module.value('gmfPrintTemplateUrl', * @return {string} Template. */ (element, attrs) => { - const templateUrl = attrs['gmfPrintTemplateurl']; + const templateUrl = attrs.gmfPrintTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/print'; }); @@ -291,14 +291,14 @@ class Controller { this.translate_ = $filter('translate'); /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {boolean} */ - this.active; + this.active = false; /** * @type {boolean} @@ -306,14 +306,14 @@ class Controller { this.rotateMask = false; /** - * @type {Object.!} + * @type {Object!} */ this.fieldValues = {}; /** - * @type {Array.} + * @type {Array} */ - this.attributesOut; + this.attributesOut = []; /** * @type {angular.IScope} @@ -433,34 +433,34 @@ class Controller { this.statusTimeoutPromise_ = null; /** - * @type {Array.|null} + * @type {?Array} * @private */ this.onDragPreviousMousePosition_ = null; /** - * @type {?angular.IPromise|null} + * @type {?angular.IPromise} * @private */ this.rotationTimeoutPromise_ = null; /** - * @type {import("ol/events.js").EventsKey} + * @type {?import("ol/events.js").EventsKey} * @private */ - this.postComposeListenerKey_; + this.postComposeListenerKey_ = null; /** - * @type {import("ol/events.js").EventsKey} + * @type {?import("ol/events.js").EventsKey} * @private */ - this.pointerDragListenerKey_; + this.pointerDragListenerKey_ = null; /** - * @type {import("ol/events.js").EventsKey} + * @type {?import("ol/events.js").EventsKey} * @private */ - this.mapViewResolutionChangeKey_; + this.mapViewResolutionChangeKey_ = null; /** * Current report reference id. @@ -471,14 +471,14 @@ class Controller { /** * Formats availables in capabilities. - * @type {Array.} + * @type {Array} * @private */ this.formats_ = []; /** * An array of attributes objects from capabilities. - * @type {Array.} + * @type {Array} * @private */ this.layouts_ = []; @@ -491,7 +491,7 @@ class Controller { this.layout_ = {}; /** - * @type {Array.} + * @type {Array} * @private */ this.paperSize_ = []; @@ -534,9 +534,9 @@ class Controller { this.smtpSupported = false; /** - * @type {Array.} + * @type {Array} */ - this.hiddenAttributeNames; + this.hiddenAttributeNames = []; /** * @type {boolean} @@ -551,40 +551,40 @@ class Controller { this.rotationInput_.on('input', (event) => { const rotation = $(event.target).val(); - if (rotation !== '') { - this.setRotation(/** @type {number} */ (rotation)); + if (typeof rotation == 'number') { + this.setRotation(rotation); } }); // Workaround for IE11 this.rotationInput_.on('change', (event) => { const rotation = $(event.target).val(); - if (rotation !== '') { - this.setRotation(/** @type {number} */ (rotation)); + if (typeof rotation == 'number') { + this.setRotation(rotation); } }); /** * @type {function((Event|import("ol/events/Event.js").default)): (void|boolean)} */ - this.postcomposeListener_; + this.postcomposeListener_ = (e) => false; /** - * @type {angular.IHttpPromise} + * @type {?angular.IHttpPromise} * @private */ - this.capabilities_; + this.capabilities_ = null; /** - * @type {import('gmf/themes.js').GmfOgcServers} + * @type {?import('gmf/themes.js').GmfOgcServers} * @private */ - this.ogcServers_; + this.ogcServers_ = null; /** - * @type {Array.} + * @type {Array} * @private */ - this.currentThemes_; + this.currentThemes_ = []; } @@ -592,6 +592,9 @@ class Controller { * Init the controller */ $onInit() { + if (!this.map) { + throw new Error('Missing map'); + } olEvents.listen(this.map.getView(), 'change:rotation', (event) => { this.updateRotation_(Math.round(olMath.toDegrees(event.target.getRotation()))); }); @@ -676,7 +679,13 @@ class Controller { if (!this.capabilities_) { this.getCapabilities_(this.gmfAuthenticationService_.getRolesIds().join(',')); } + if (!this.capabilities_) { + throw new Error('Missing capabilities'); + } this.capabilities_.then((resp) => { + if (!this.map) { + throw new Error('Missing map'); + } // make sure the panel is still open if (!this.active) { return; @@ -696,9 +705,18 @@ class Controller { this.capabilities_ = null; }); } else { - olEvents.unlistenByKey(this.postComposeListenerKey_); - olEvents.unlistenByKey(this.pointerDragListenerKey_); - olEvents.unlistenByKey(this.mapViewResolutionChangeKey_); + if (!this.map) { + throw new Error('Missing map'); + } + if (this.postComposeListenerKey_) { + olEvents.unlistenByKey(this.postComposeListenerKey_); + } + if (this.pointerDragListenerKey_) { + olEvents.unlistenByKey(this.pointerDragListenerKey_); + } + if (this.mapViewResolutionChangeKey_) { + olEvents.unlistenByKey(this.mapViewResolutionChangeKey_); + } this.setRotation(0); this.map.render(); // Redraw (remove) post compose mask; } @@ -729,17 +747,17 @@ class Controller { * @private */ parseCapabilities_(resp) { - const data = resp['data']; - this.formats_ = data['formats'] || []; - this.layouts_ = data['layouts']; - this.layout_ = data['layouts'][0]; + const data = resp.data; + this.formats_ = data.formats || []; + this.layouts_ = data.layouts; + this.layout_ = data.layouts[0]; this.layoutInfo.layouts = []; this.layouts_.forEach((layout) => { this.layoutInfo.layouts.push(layout.name); }); - this.smtpSupported = data['smtp'] && data['smtp']['enabled']; + this.smtpSupported = data.smtp && data.smtp.enabled; this.updateFields_(); } @@ -754,20 +772,27 @@ class Controller { * @private */ updateFields_() { + if (!this.map) { + throw new Error('Missing map'); + } this.layoutInfo.layout = this.layout_.name; const mapInfo = this.isAttributeInCurrentLayout_('map'); console.assert(mapInfo); - const clientInfo = mapInfo['clientInfo']; + const clientInfo = mapInfo.clientInfo; console.assert(clientInfo); - this.paperSize_ = [clientInfo['width'], clientInfo['height']]; + this.paperSize_ = [clientInfo.width, clientInfo.height]; this.updateCustomFields_(); - this.layoutInfo.legend = this.layoutInfo.attributes.indexOf('legend') >= 0 ? - this.fieldValues['legend'] !== false : undefined; - this.layoutInfo.scales = clientInfo['scales'] || []; - this.layoutInfo.dpis = clientInfo['dpiSuggestions'] || []; + const hasLegend = this.layoutInfo.attributes.indexOf('legend') >= 0; + if (hasLegend) { + this.fieldValues.legend = this.fieldValues.legend; + } else { + delete this.fieldValues.legend; + } + this.layoutInfo.scales = clientInfo.scales || []; + this.layoutInfo.dpis = clientInfo.dpiSuggestions || []; const mapSize = this.map.getSize(); const viewResolution = this.map.getView().getResolution(); @@ -807,7 +832,7 @@ class Controller { // The attributes without 'clientParams' are the custom layout information (defined by end user). this.layout_.attributes.forEach((attribute) => { this.layoutInfo.attributes.push(attribute.name); - if (!attribute['clientParams']) { + if (!attribute.clientParams) { name = `${attribute.name}`; const defaultValue = attribute.default; value = (defaultValue !== undefined && defaultValue !== '') ? @@ -851,7 +876,7 @@ class Controller { /** * Return a capabilities 'attribute' object corresponding to the given name. * @param {string} name Name of the attribute to get. - * @return {Object|null} corresponding attribute or null. + * @return {?Object} corresponding attribute or null. * @private */ isAttributeInCurrentLayout_(name) { @@ -871,6 +896,9 @@ class Controller { * @param {number} rotation The optional new rotation value in degrees. */ setRotation(rotation) { + if (!this.map) { + throw new Error('Missing map'); + } this.updateRotation_(rotation); // Render the map to update the postcompose mask or rotate the map. if (this.rotateMask) { @@ -894,10 +922,16 @@ class Controller { * Calculate the angle and the sense of rotation between two lines. One from the * center of the map and the point of the last call to this function and one * from the same center and the point of the current call. - * @param {event|import("ol/events/Event.js").default} e An ol map browser pointer event. + * @param {Event|import("ol/events/Event.js").default} e An ol map browser pointer event. * @private */ onPointerDrag_(e) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.onDragPreviousMousePosition_) { + throw new Error('Missing onDragPreviousMousePosition'); + } if (e instanceof MapBrowserPointerEvent) { const originalEvent = e.originalEvent; if (originalEvent instanceof KeyboardEvent) { @@ -946,6 +980,12 @@ class Controller { * capabilities document ('pdf', 'png', etc). */ print(format) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.ogcServers_) { + throw new Error('Missing ogcServers_'); + } // Do not print if a print task is already processing. if (this.gmfPrintState_.state === PrintStateEnum.PRINTING) { return; @@ -961,7 +1001,7 @@ class Controller { const customAttributes = {}; if (this.layoutInfo.attributes.indexOf('datasource') >= 0) { - customAttributes['datasource'] = datasource; + customAttributes.datasource = datasource; } if (this.layoutInfo.simpleAttributes) { @@ -972,6 +1012,9 @@ class Controller { if (this.layoutInfo.legend) { const center = this.map.getView().getCenter(); + if (!center) { + throw new Error('Missing center'); + } const deltaX = this.paperSize_[0] * scale / 2 / INCHES_PER_METER / DOTS_PER_INCH; const deltaY = this.paperSize_[1] * scale / 2 / INCHES_PER_METER / DOTS_PER_INCH; const bbox = [ @@ -982,7 +1025,7 @@ class Controller { ]; const legend = this.getLegend_(scale, this.layoutInfo.dpi, bbox); if (legend !== null) { - customAttributes['legend'] = legend; + customAttributes.legend = legend; } } @@ -1057,7 +1100,7 @@ class Controller { ); // remove temporary map - map.setTarget(null); + map.setTarget(''); } @@ -1232,7 +1275,10 @@ class Controller { * @private */ getLegend_(scale, dpi, bbox) { - const legend = {'classes': []}; + if (!this.map) { + throw new Error('Missing map'); + } + const legend = {classes: /** @type {Array} */([])}; const gettextCatalog = this.gettextCatalog_; // Get layers from layertree only. @@ -1241,6 +1287,9 @@ class Controller { // For each visible layer in reverse order, get the legend url. layers.reverse().forEach((layer) => { + if (!this.map) { + throw new Error('Missing map'); + } const classes = []; if (layer.getVisible() && layer.getSource()) { // For WMTS layers. @@ -1251,34 +1300,43 @@ class Controller { const url = this.ngeoLayerHelper_.getWMTSLegendURL(layer); if (url) { icon_dpi = { - 'url': this.ngeoLayerHelper_.getWMTSLegendURL(layer), - 'dpi': 72 + url: url, + dpi: 72 }; } } // Don't add classes without legend url. if (icon_dpi) { classes.push({ - 'name': gettextCatalog.getString(layerName), - 'icons': [icon_dpi.url] + name: gettextCatalog.getString(layerName), + icons: [icon_dpi.url] }); } } else { const source = /** @type import("ol/source/ImageWMS.js").default */ (layer.getSource()); // For each name in a WMS layer. - const layerNames = source.getParams()['LAYERS'].split(','); + const layerNames = source.getParams().LAYERS.split(','); layerNames.forEach((name) => { + if (!this.map) { + throw new Error('Missing map'); + } + if (!source.serverType_) { + throw new Error('Missing source.serverType_'); + } let icon_dpi = this.getMetadataLegendImage_(name, dpi); const type = icon_dpi ? 'image' : source.serverType_; if (!icon_dpi) { + const url = this.ngeoLayerHelper_.getWMSLegendURL(source.getUrl(), name, + scale, undefined, undefined, undefined, source.serverType_, dpi, + this.gmfLegendOptions_.useBbox ? bbox : undefined, + this.map.getView().getProjection().getCode(), + this.gmfLegendOptions_.params[source.serverType_]); + if (!url) { + throw new Error('Missing url'); + } icon_dpi = { - 'url': this.ngeoLayerHelper_.getWMSLegendURL(source.getUrl(), name, - scale, undefined, undefined, undefined, source.serverType_, dpi, - this.gmfLegendOptions_.useBbox ? bbox : undefined, - this.map.getView().getProjection().getCode(), - this.gmfLegendOptions_.params[source.serverType_] - ), - 'dpi': type === 'qgis' ? dpi : 72, + url: url, + dpi: type === 'qgis' ? dpi : 72, }; } @@ -1286,11 +1344,11 @@ class Controller { // active name. if (icon_dpi && name.length !== 0) { classes.push(Object.assign({ - 'name': this.gmfLegendOptions_.label[type] === false ? '' : + name: this.gmfLegendOptions_.label[type] === false ? '' : gettextCatalog.getString(name), - 'icons': [icon_dpi.url] + icons: [icon_dpi.url] }, icon_dpi.dpi != 72 ? { - 'dpi': icon_dpi.dpi, + dpi: icon_dpi.dpi, } : {})); } }); @@ -1299,12 +1357,11 @@ class Controller { // Add classes object only if it contains something. if (classes.length > 0) { - legend['classes'].push({'classes': classes}); + legend.classes.push({classes: classes}); } - }); - return legend['classes'].length > 0 ? legend : null; + return legend.classes.length > 0 ? legend : null; } /** @@ -1348,9 +1405,12 @@ class Controller { } } } + if (!legendImage) { + throw new Error('Missing legendImage'); + } return { - 'url': legendImage, - 'dpi': dpi, + url: legendImage, + dpi: dpi, }; } @@ -1380,6 +1440,9 @@ class Controller { */ getSetScale(opt_scale) { if (opt_scale !== undefined) { + if (!this.map) { + throw new Error('Missing map'); + } const mapSize = this.map.getSize() || [0, 0]; this.layoutInfo.scale = opt_scale; const res = this.ngeoPrintUtils_.getOptimalResolution(mapSize, this.paperSize_, opt_scale); diff --git a/contribs/gmf/src/profile/component.js b/contribs/gmf/src/profile/component.js index d9e46acf054..d730526b4c7 100644 --- a/contribs/gmf/src/profile/component.js +++ b/contribs/gmf/src/profile/component.js @@ -57,7 +57,7 @@ module.value('gmfProfileTemplateUrl', * @return {string} Template. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfProfileTemplateurl']; + const templateUrl = $attrs.gmfProfileTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/profile'; }); @@ -205,19 +205,19 @@ function Controller($scope, $http, $element, $filter, gettextCatalog, ngeoFeatur this.ngeoCsvDownload_ = ngeoCsvDownload; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} * @private */ this.map_ = null; /** - * @type {?Object} + * @type {?Object} * @private */ this.linesConfiguration_ = null; /** - * @type {!Array.} + * @type {Array} * @private */ this.layersNames_ = []; @@ -229,12 +229,12 @@ function Controller($scope, $http, $element, $filter, gettextCatalog, ngeoFeatur this.nbPoints_ = 100; /** - * @type {import("ol/geom/LineString.js").default} + * @type {?import("ol/geom/LineString.js").default} */ - this.line; + this.line = null; /** - * @type {Array.} + * @type {Array} */ this.profileData = []; @@ -242,11 +242,7 @@ function Controller($scope, $http, $element, $filter, gettextCatalog, ngeoFeatur * @type {ProfileHoverPointInformations} */ this.currentPoint = { - coordinate: undefined, - distance: undefined, elevations: {}, - xUnits: undefined, - yUnits: undefined }; /** @@ -257,14 +253,14 @@ function Controller($scope, $http, $element, $filter, gettextCatalog, ngeoFeatur /** * Overlay to show the measurement. - * @type {import("ol/Overlay.js").default} + * @type {?import("ol/Overlay.js").default} * @private */ this.measureTooltip_ = null; /** * The measure tooltip element. - * @type {HTMLElement} + * @type {?HTMLElement} * @private */ this.measureTooltipElement_ = null; @@ -297,16 +293,22 @@ function Controller($scope, $http, $element, $filter, gettextCatalog, ngeoFeatur this.active = false; /** - * @type {import("ol/events.js").EventsKey} + * @type {?import("ol/events.js").EventsKey} * @private */ - this.pointerMoveKey_; + this.pointerMoveKey_ = null; /** * @type {boolean} */ this.isErrored = false; + this.getMapFn = () => null; + this.getNbPointsFn = () => 100; + this.getHoverPointStyleFn = null; + this.getLinesConfigurationFn = () => ({}); + this.getOptionsFn = () => ({}); + // Watch the active value to activate/deactivate events listening. $scope.$watch( @@ -334,11 +336,11 @@ function Controller($scope, $http, $element, $filter, gettextCatalog, ngeoFeatur * Init the controller */ Controller.prototype.$onInit = function() { - this.map_ = this['getMapFn'] ? this['getMapFn']() : null; - this.nbPoints_ = this['getNbPointsFn'] ? this['getNbPointsFn']() : 100; + this.map_ = this.getMapFn(); + this.nbPoints_ = this.getNbPointsFn(); let hoverPointStyle; - const hoverPointStyleFn = this['getHoverPointStyleFn']; + const hoverPointStyleFn = this.getHoverPointStyleFn; if (hoverPointStyleFn) { hoverPointStyle = hoverPointStyleFn(); console.assert(hoverPointStyle instanceof olStyleStyle); @@ -352,7 +354,7 @@ Controller.prototype.$onInit = function() { } this.pointHoverOverlay_.setStyle(hoverPointStyle); - const linesConfiguration = this['getLinesConfigurationFn'](); + const linesConfiguration = this.getLinesConfigurationFn(); console.assert(linesConfiguration instanceof Object); this.linesConfiguration_ = linesConfiguration; @@ -375,7 +377,7 @@ Controller.prototype.$onInit = function() { i18n: this.profileLabels_ }); - const optionsFn = this['getOptionsFn']; + const optionsFn = this.getOptionsFn; if (optionsFn) { const options = optionsFn(); console.assert(options); @@ -404,33 +406,36 @@ Controller.prototype.update_ = function() { Controller.prototype.updateEventsListening_ = function() { if (this.active && this.map_ !== null) { this.pointerMoveKey_ = olEvents.listen(this.map_, 'pointermove', - this.onPointerMove_.bind(this)); - } else { - olEvents.unlistenByKey(this.pointerMoveKey_); - } -}; - - -/** - * @param {import("ol/MapBrowserPointerEvent.js").default} e An ol map browser pointer event. - * @private - */ -Controller.prototype.onPointerMove_ = function(e) { - if (e.dragging || !this.line) { - return; - } - const coordinate = this.map_.getEventCoordinate(e.originalEvent); - const closestPoint = this.line.getClosestPoint(coordinate); - // compute distance to line in pixels - const eventToLine = new olGeomLineString([closestPoint, coordinate]); - const pixelDist = eventToLine.getLength() / this.map_.getView().getResolution(); - - if (pixelDist < 16) { - this.profileHighlight = this.getDistanceOnALine_(closestPoint); + (event) => { + const e = /** @type {import("ol/MapBrowserPointerEvent.js").default} */(event); + if (!this.map_) { + throw new Error('Missing map'); + } + if (e.dragging || !this.line) { + return; + } + const coordinate = this.map_.getEventCoordinate(e.originalEvent); + const closestPoint = this.line.getClosestPoint(coordinate); + // compute distance to line in pixels + const eventToLine = new olGeomLineString([closestPoint, coordinate]); + const resolution = this.map_.getView().getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } + const pixelDist = eventToLine.getLength() / resolution; + + if (pixelDist < 16) { + this.profileHighlight = this.getDistanceOnALine_(closestPoint); + } else { + this.profileHighlight = -1; + } + this.$scope_.$apply(); + }); } else { - this.profileHighlight = -1; + if (this.pointerMoveKey_) { + olEvents.unlistenByKey(this.pointerMoveKey_); + } } - this.$scope_.$apply(); }; @@ -443,6 +448,9 @@ Controller.prototype.onPointerMove_ = function(e) { * @private */ Controller.prototype.getDistanceOnALine_ = function(pointOnLine) { + if (!this.line) { + throw new Error('Missing line'); + } let segment; let distOnLine = 0; const fakeExtent = [ @@ -477,6 +485,12 @@ Controller.prototype.getDistanceOnALine_ = function(pointOnLine) { * @private */ Controller.prototype.hoverCallback_ = function(point, dist, xUnits, elevationsRef, yUnits) { + if (!this.measureTooltipElement_) { + throw new Error('Missing measureTooltipElement_'); + } + if (!this.measureTooltip_) { + throw new Error('Missing measureTooltip_'); + } // Update information point. const coordinate = [point.x, point.y]; @@ -500,15 +514,15 @@ Controller.prototype.hoverCallback_ = function(point, dist, xUnits, elevationsRe */ Controller.prototype.outCallback_ = function() { // Reset information point. - this.currentPoint.coordinate = undefined; - this.currentPoint.distance = undefined; + delete this.currentPoint.coordinate; + delete this.currentPoint.distance; this.currentPoint.elevations = {}; - this.currentPoint.xUnits = undefined; - this.currentPoint.yUnits = undefined; + delete this.currentPoint.xUnits; + delete this.currentPoint.yUnits; // Reset hover. this.removeMeasureTooltip_(); - this.snappedPoint_.setGeometry(null); + this.snappedPoint_.setGeometry(undefined); }; @@ -542,6 +556,9 @@ Controller.prototype.getTooltipHTML_ = function() { * @private */ Controller.prototype.createMeasureTooltip_ = function() { + if (!this.map_) { + throw new Error('Missing map'); + } this.removeMeasureTooltip_(); this.measureTooltipElement_ = document.createElement('div'); this.measureTooltipElement_.className += 'tooltip ngeo-tooltip-measure'; @@ -560,6 +577,15 @@ Controller.prototype.createMeasureTooltip_ = function() { */ Controller.prototype.removeMeasureTooltip_ = function() { if (this.measureTooltipElement_ !== null) { + if (!this.map_) { + throw new Error('Missing map'); + } + if (!this.measureTooltip_) { + throw new Error('Missing measureTooltip_'); + } + if (!this.measureTooltipElement_.parentNode) { + throw new Error('Missing measureTooltipElement_.parentNode'); + } this.measureTooltipElement_.parentNode.removeChild(this.measureTooltipElement_); this.measureTooltipElement_ = null; this.map_.removeOverlay(this.measureTooltip_); @@ -573,6 +599,9 @@ Controller.prototype.removeMeasureTooltip_ = function() { * @return {object} The object representation of the style. */ Controller.prototype.getStyle = function(layerName) { + if (!this.linesConfiguration_) { + throw new Error('Missing linesConfiguration'); + } const lineConfiguration = this.linesConfiguration_[layerName]; if (!lineConfiguration) { return {}; @@ -594,7 +623,7 @@ Controller.prototype.getLayersNames = function() { /** * @param {string} layerName name of the elevation layer. - * @return {function(Object):number} Z extractor function. + * @return {function(Object): number} Z extractor function. * @private */ Controller.prototype.getZFactory_ = function(layerName) { @@ -605,10 +634,10 @@ Controller.prototype.getZFactory_ = function(layerName) { * @private */ const getZFn = function(item) { - if ('values' in item && layerName in item['values'] && item['values'][layerName]) { - return parseFloat(item['values'][layerName]); + if ('values' in item && layerName in item.values && item.values[layerName]) { + return parseFloat(item.values[layerName]); } - return null; + throw new Error('Unexpected'); }; return getZFn; }; @@ -622,7 +651,7 @@ Controller.prototype.getZFactory_ = function(layerName) { */ Controller.prototype.getDist_ = function(item) { if ('dist' in item) { - return item['dist']; + return item.dist; } return 0; }; @@ -633,6 +662,9 @@ Controller.prototype.getDist_ = function(item) { * @private */ Controller.prototype.getJsonProfile_ = function() { + if (!this.line) { + throw new Error('Missing line'); + } const geom = { 'type': 'LineString', 'coordinates': this.line.getCoordinates() @@ -664,7 +696,7 @@ Controller.prototype.getJsonProfile_ = function() { * @private */ Controller.prototype.getProfileDataSuccess_ = function(resp) { - const profileData = resp.data['profile']; + const profileData = resp.data.profile; if (profileData instanceof Array) { this.profileData = profileData; } @@ -698,7 +730,7 @@ Controller.prototype.downloadCsv = function() { hasDistance = true; } const layers = []; - for (const layer in firstPoint['values']) { + for (const layer in firstPoint.values) { headers.push({'name': layer}); layers.push(layer); } @@ -708,15 +740,15 @@ Controller.prototype.downloadCsv = function() { const rows = this.profileData.map((point) => { const row = {}; if (hasDistance) { - row['distance'] = point['dist']; + row.distance = point.dist; } layers.forEach((layer) => { - row[layer] = point['values'][layer]; + row[layer] = point.values[layer]; }); - row['x'] = point['x']; - row['y'] = point['y']; + row.x = point.x; + row.y = point.y; return row; }); diff --git a/contribs/gmf/src/profile/drawLineComponent.js b/contribs/gmf/src/profile/drawLineComponent.js index 542e5493005..0e5f0b29fb6 100644 --- a/contribs/gmf/src/profile/drawLineComponent.js +++ b/contribs/gmf/src/profile/drawLineComponent.js @@ -75,7 +75,7 @@ function Controller($scope, $timeout, ngeoFeatureOverlayMgr) { /** * @type {?import("ol/geom/LineString.js").default} */ - this.line; + this.line = null; /** * @type {?import("ol/Map.js").default} @@ -86,19 +86,23 @@ function Controller($scope, $timeout, ngeoFeatureOverlayMgr) { /** * @type {boolean} */ - this.active; + this.active = false; /** - * @type {!import("ol/Collection.js").default} + * @type {import("ol/Collection.js").default} * @private */ this.features_ = new olCollection(); + this.getMapFn = () => null; + + this.getStyleFn = null; + const overlay = ngeoFeatureOverlayMgr.getFeatureOverlay(); overlay.setFeatures(this.features_); let style; - const styleFn = this['getStyleFn']; + const styleFn = this.getStyleFn; if (styleFn) { style = styleFn(); console.assert(style instanceof olStyleStyle); @@ -165,8 +169,10 @@ function Controller($scope, $timeout, ngeoFeatureOverlayMgr) { * Initialise the controller. */ Controller.prototype.$onInit = function() { - const map = this['getMapFn'](); - console.assert(map instanceof olMap); + const map = this.getMapFn(); + if (!(map instanceof olMap)) { + throw 'Wrong map'; + } this.map_ = map; this.map_.addInteraction(this.interaction); }; diff --git a/contribs/gmf/src/query/gridComponent.js b/contribs/gmf/src/query/gridComponent.js index a3c677499ad..347c582fbe2 100644 --- a/contribs/gmf/src/query/gridComponent.js +++ b/contribs/gmf/src/query/gridComponent.js @@ -25,7 +25,7 @@ import 'bootstrap/js/src/dropdown.js'; /** * Configuration for a grid tab. * @typedef {Object} GridSource - * @property {import("ngeo/grid/Config.js").default} configuration Configuration used to initialize a grid. + * @property {import("ngeo/grid/Config.js").default} [configuration] Configuration used to initialize a grid. * @property {import('ngeo/statemanager/WfsPermalink.js').QueryResultSource} source Results of the query * source. */ @@ -43,7 +43,7 @@ import 'bootstrap/js/src/dropdown.js'; /** - * @type {!angular.IModule} + * @type {angular.IModule} * @hidden */ const module = angular.module('gmfQueryGridComponent', [ @@ -57,12 +57,12 @@ const module = angular.module('gmfQueryGridComponent', [ module.value('gmfDisplayquerygridTemplateUrl', /** - * @param {!JQuery} $element Element. - * @param {!angular.IAttributes} $attrs Attributes. + * @param {JQuery} $element Element. + * @param {angular.IAttributes} $attrs Attributes. * @return {string} Template URL. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfDisplayquerygridTemplateurl']; + const templateUrl = $attrs.gmfDisplayquerygridTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/query/gridComponent'; } @@ -237,7 +237,7 @@ export function QueryGridController($injector, $scope, ngeoQueryResult, ngeoMapQ /** * The id of the currently shown query source. - * @type {string|number|null} + * @type {?string|number} */ this.selectedTab = null; @@ -248,12 +248,12 @@ export function QueryGridController($injector, $scope, ngeoQueryResult, ngeoMapQ this.removeEmptyColumns_ = false; /** - * @type {number|undefined} + * @type {?number} */ - this.maxRecenterZoom; + this.maxRecenterZoom = null; /** - * @type {!GridMergeTabs} + * @type {GridMergeTabs} */ this.mergeTabs = {}; @@ -294,7 +294,7 @@ export function QueryGridController($injector, $scope, ngeoQueryResult, ngeoMapQ $injector.get('gmfCsvFilename') : 'query-results.csv'; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} * @private */ this.map_ = null; @@ -315,18 +315,24 @@ export function QueryGridController($injector, $scope, ngeoQueryResult, ngeoMapQ * @private */ this.unregisterSelectWatcher_ = null; + + this.removeEmptyColumnsFn = () => false; + this.maxRecenterZoomFn = () => null; + this.featuresStyleFn = () => null; + this.selectedFeatureStyleFn = () => null; + this.getMapFn = () => null; } /** * Init the controller */ QueryGridController.prototype.$onInit = function() { - this.removeEmptyColumns_ = this['removeEmptyColumnsFn'] ? this['removeEmptyColumnsFn']() === true : false; - this.maxRecenterZoom = this['maxRecenterZoomFn'] ? this['maxRecenterZoomFn']() : undefined; + this.removeEmptyColumns_ = this.removeEmptyColumnsFn(); + this.maxRecenterZoom = this.maxRecenterZoomFn(); const featuresOverlay = this.ngeoFeatureOverlayMgr_.getFeatureOverlay(); featuresOverlay.setFeatures(this.features_); - const featuresStyle = this['featuresStyleFn'](); + const featuresStyle = this.featuresStyleFn(); if (featuresStyle !== undefined) { console.assert(featuresStyle instanceof olStyleStyle); featuresOverlay.setStyle(featuresStyle); @@ -334,7 +340,7 @@ QueryGridController.prototype.$onInit = function() { const highlightFeaturesOverlay = this.ngeoFeatureOverlayMgr_.getFeatureOverlay(); highlightFeaturesOverlay.setFeatures(this.highlightFeatures_); - let highlightFeatureStyle = this['selectedFeatureStyleFn'](); + let highlightFeatureStyle = this.selectedFeatureStyleFn(); if (highlightFeatureStyle !== undefined) { console.assert(highlightFeatureStyle instanceof olStyleStyle); } else { @@ -353,7 +359,7 @@ QueryGridController.prototype.$onInit = function() { } highlightFeaturesOverlay.setStyle(highlightFeatureStyle); - const mapFn = this['getMapFn']; + const mapFn = this.getMapFn; if (mapFn) { const map = mapFn(); console.assert(map instanceof olMap); @@ -539,7 +545,6 @@ QueryGridController.prototype.getMergedSource_ = function(source, mergedSources) pending: false, queried: true, tooManyResults: false, - totalFeatureCount: undefined }; mergedSources[mergeSourceId] = mergeSource; } @@ -613,8 +618,8 @@ QueryGridController.prototype.cleanProperties_ = function(allProperties, feature featureGeometriesNames.forEach((featureGeometryName) => { delete properties[featureGeometryName]; }); - delete properties['boundedBy']; - delete properties['ngeo_feature_type_']; + delete properties.boundedBy; + delete properties.ngeo_feature_type_; }); if (this.removeEmptyColumns_ === true) { @@ -676,9 +681,11 @@ QueryGridController.prototype.makeGrid_ = function(data, source) { this.loadedGridSources.push(sourceLabel); } this.gridSources[sourceLabel] = { - configuration: gridConfig, source: source }; + if (gridConfig) { + this.gridSources[sourceLabel].configuration = gridConfig; + } return true; }; @@ -746,7 +753,7 @@ QueryGridController.prototype.selectTab = function(gridSource) { this.unregisterSelectWatcher_ = null; } - if (gridSource.configuration !== null) { + if (gridSource.configuration !== undefined) { this.unregisterSelectWatcher_ = this.$scope_.$watchCollection( () => gridSource.configuration.selectedRows, (newSelected, oldSelectedRows) => { @@ -774,7 +781,7 @@ QueryGridController.prototype.reflowGrid_ = function() { const activePane = this.$element_.find(`div.tab-pane#${id}`); activePane.removeClass('active').addClass('active'); this.$timeout_(() => { - activePane.find('div.ngeo-grid-table-container table')['trigger']('reflow'); + activePane.find('div.ngeo-grid-table-container table').trigger('reflow'); }); }; @@ -801,7 +808,7 @@ QueryGridController.prototype.updateFeatures_ = function(gridSource) { this.features_.clear(); this.highlightFeatures_.clear(); - if (gridSource.configuration === null) { + if (!gridSource.configuration) { return; } @@ -898,6 +905,9 @@ QueryGridController.prototype.invertSelection = function() { * Zoom to the selected features. */ QueryGridController.prototype.zoomToSelection = function() { + if (!this.map_) { + throw new Error('Missing map'); + } const source = this.getActiveGridSource(); if (source !== null) { const extent = olExtent.createEmpty(); @@ -905,9 +915,13 @@ QueryGridController.prototype.zoomToSelection = function() { olExtent.extend(extent, feature.getGeometry().getExtent()); }); const size = this.map_.getSize(); - console.assert(size !== undefined); - const maxZoom = this.maxRecenterZoom; - this.map_.getView().fit(extent, {size, maxZoom}); + if (!size) { + throw new Error('Missing size'); + } + if (this.maxRecenterZoom === null) { + throw new Error('Missing maxRecenterZoom'); + } + this.map_.getView().fit(extent, {size, maxZoom: this.maxRecenterZoom}); } }; @@ -919,7 +933,9 @@ QueryGridController.prototype.downloadCsv = function() { const source = this.getActiveGridSource(); if (source !== null) { const columnDefs = source.configuration.columnDefs; - console.assert(columnDefs !== undefined); + if (!columnDefs) { + throw new Error('Missing columnDefs'); + } const selectedRows = source.configuration.getSelectedRows(); this.ngeoCsvDownload_.startDownload( diff --git a/contribs/gmf/src/query/windowComponent.js b/contribs/gmf/src/query/windowComponent.js index 283f9cabcfe..e3ec300e8db 100644 --- a/contribs/gmf/src/query/windowComponent.js +++ b/contribs/gmf/src/query/windowComponent.js @@ -53,7 +53,7 @@ module.value('gmfDisplayquerywindowTemplateUrl', * @return {string} Template. */ ($element, $attrs) => { - const templateUrl = $attrs['gmfDisplayquerywindowTemplateurl']; + const templateUrl = $attrs.gmfDisplayquerywindowTemplateurl; return templateUrl !== undefined ? templateUrl : 'gmf/query/windowComponent'; }); @@ -147,7 +147,7 @@ export function QueryWindowController( /** * @type {Element|string} */ - this.draggableContainment; + this.draggableContainment = ''; /** * @type {boolean} @@ -247,6 +247,13 @@ export function QueryWindowController( */ this.element_ = $element; + this.defaultCollapsedFn = () => !this.desktop; + + this.showUnqueriedLayers = false; + + this.featuresStyleFn = null; + this.selectedFeatureStyleFn = null; + $scope.$watchCollection( () => ngeoQueryResult, (newQueryResult, oldQueryResult) => { @@ -265,17 +272,15 @@ export function QueryWindowController( QueryWindowController.prototype.$onInit = function() { this.draggableContainment = this.draggableContainment || 'document'; this.desktop = this.desktop; - this.collapsed = this['defaultCollapsedFn'] ? - this['defaultCollapsedFn']() === true : !this.desktop; + this.collapsed = this.defaultCollapsedFn(); - this.showUnqueriedLayers_ = this['showUnqueriedLayers'] ? - this['showUnqueriedLayers'] === true : false; + this.showUnqueriedLayers_ = this.showUnqueriedLayers; this.sourcesFilter = this.showUnqueriedLayers_ ? {} : {'queried': true}; const featuresOverlay = this.ngeoFeatureOverlayMgr_.getFeatureOverlay(); featuresOverlay.setFeatures(this.features_); - const featuresStyle = this['featuresStyleFn'](); + const featuresStyle = this.featuresStyleFn(); if (featuresStyle !== undefined) { console.assert(featuresStyle instanceof olStyleStyle); featuresOverlay.setStyle(featuresStyle); @@ -283,7 +288,7 @@ QueryWindowController.prototype.$onInit = function() { const highlightFeaturesOverlay = this.ngeoFeatureOverlayMgr_.getFeatureOverlay(); highlightFeaturesOverlay.setFeatures(this.highlightFeatures_); - let highlightFeatureStyle = this['selectedFeatureStyleFn'](); + let highlightFeatureStyle = this.selectedFeatureStyleFn(); if (highlightFeatureStyle !== undefined) { console.assert(highlightFeatureStyle instanceof olStyleStyle); } else { @@ -370,7 +375,7 @@ QueryWindowController.prototype.setCurrentResult_ = function( } } if (setHighlight) { - this.highlightCurrentFeature_(lastFeature); + this.highlightCurrentFeature_(lastFeature || undefined); } } return hasChanged; diff --git a/contribs/gmf/src/raster/component.js b/contribs/gmf/src/raster/component.js index 2cdcfe7f0fc..b6f0a75d3d6 100644 --- a/contribs/gmf/src/raster/component.js +++ b/contribs/gmf/src/raster/component.js @@ -30,7 +30,7 @@ module.value('gmfElevationwidgetTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfElevationwidgetTemplateUrl']; + const templateUrl = $attrs.gmfElevationwidgetTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/raster/widgetComponent'; }); @@ -164,30 +164,30 @@ function Controller($scope, $filter, ngeoDebounce, gmfRaster, gettextCatalog) { this.gettextCatalog = gettextCatalog; /** - * @type {!Object.} + * @type {Object} * @private */ - this.layersconfig; + this.layersconfig = {}; /** * @type {boolean} */ - this.active; + this.active = false; /** - * @type {!string|undefined} + * @type {?string} */ - this.elevation; + this.elevation = null; /** * @type {string} */ - this.layer; + this.layer = ''; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {Array.} @@ -219,9 +219,12 @@ function Controller($scope, $filter, ngeoDebounce, gmfRaster, gettextCatalog) { * @private */ Controller.prototype.toggleActive_ = function(active) { - this.elevation = undefined; + this.elevation = null; if (active) { console.assert(this.listenerKeys_.length === 0); + if (!this.map) { + throw new Error('Missing map'); + } // Moving the mouse clears previously displayed elevation /** @@ -231,7 +234,7 @@ Controller.prototype.toggleActive_ = function(active) { const listen = (event) => { this.scope_.$apply(() => { this.inViewport_ = true; - this.elevation = undefined; + this.elevation = null; this.loading = false; }); }; @@ -240,44 +243,35 @@ Controller.prototype.toggleActive_ = function(active) { // Launch the elevation service request when the user stops moving the // mouse for less short delay this.listenerKeys_.push(olEvents.listen(this.map, 'pointermove', - this.ngeoDebounce_(this.pointerStop_.bind(this), 500, true) + this.ngeoDebounce_((e) => { + if (this.inViewport_) { + this.loading = true; + const params = { + 'layers': this.layer + }; + this.gmfRaster_.getRaster(e.coordinate, params).then( + this.getRasterSuccess_.bind(this), + this.getRasterError_.bind(this) + ); + } + }, 500, true) )); this.listenerKeys_.push(olEvents.listen(this.map.getViewport(), 'mouseout', () => { this.scope_.$apply(() => { - this.elevation = undefined; + this.elevation = null; this.inViewport_ = false; this.loading = false; }); })); } else { - this.elevation = undefined; + this.elevation = null; this.listenerKeys_.forEach(olEvents.unlistenByKey); this.listenerKeys_.length = 0; } }; -/** - * Request data from a raster from a MapBrowserPointerEvent's coordinates. - * Called when the user stopped moving the mouse for 500ms. - * @param {import("ol/MapBrowserPointerEvent.js").default} e An ol map browser pointer event. - * @private - */ -Controller.prototype.pointerStop_ = function(e) { - if (this.inViewport_) { - this.loading = true; - const params = { - 'layers': this.layer - }; - this.gmfRaster_.getRaster(e.coordinate, params).then( - this.getRasterSuccess_.bind(this), - this.getRasterError_.bind(this) - ); - } -}; - - /** * @param {Object.} resp Response of the get Raster service. * @private @@ -307,7 +301,7 @@ Controller.prototype.getRasterSuccess_ = function(resp) { */ Controller.prototype.getRasterError_ = function() { console.error('Error on getting the raster.'); - this.elevation = undefined; + this.elevation = null; this.loading = false; }; @@ -357,30 +351,30 @@ module.component('gmfElevationwidget', rasterWidgetComponent); */ function WidgetController() { /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {!Array.} + * @type {Array} */ - this.layers; + this.layers = []; /** - * @type {!Object.} + * @type {Object} * @private */ - this.layersconfig; + this.layersconfig = {}; /** * @type {boolean} */ - this.active; + this.active = false; /** * @type {string} */ - this.selectedElevationLayer; + this.selectedElevationLayer = ''; } diff --git a/contribs/gmf/src/search/component.js b/contribs/gmf/src/search/component.js index f70c3481b67..c1210b38572 100644 --- a/contribs/gmf/src/search/component.js +++ b/contribs/gmf/src/search/component.js @@ -332,9 +332,9 @@ class SearchController { this.ngeoAutoProjection_ = ngeoAutoProjection; /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {Object} @@ -343,31 +343,31 @@ class SearchController { this.styles_ = {}; /** - * @type {function(): void} + * @type {?function(): void} */ - this.onInitCallback; + this.onInitCallback = null; /** * Whether or not to show a button to clear the search text. * Default to true. * @type {boolean} */ - this.clearButton; + this.clearButton = false; /** * @type {boolean} */ - this.colorChooser; + this.colorChooser = false; /** * @type {string} */ - this.placeholder; + this.placeholder = ''; /** * @type {number} */ - this.delay; + this.delay = 0; /** * The maximum zoom we will zoom on result. @@ -379,13 +379,13 @@ class SearchController { * Supported projections for coordinates search. * @type {Array} */ - this.coordinatesProjections; + this.coordinatesProjections = []; /** * Supported projections for coordinates search. * @type {Array} */ - this.coordinatesProjectionsInstances; + this.coordinatesProjectionsInstances = []; /** * @type {import("ngeo/map/FeatureOverlay.js").FeatureOverlay} @@ -399,9 +399,9 @@ class SearchController { this.datasources = []; /** - * @type {Twitter.Typeahead.Options} + * @type {?Twitter.Typeahead.Options} */ - this.typeaheadOptions; + this.typeaheadOptions = null; /** * @type {Twitter.Typeahead.Options} @@ -411,12 +411,12 @@ class SearchController { }); /** - * @type {Object.} + * @type {Object} */ - this.featuresStyles; + this.featuresStyles = {}; /** - * @type {Array.} + * @type {Array} */ this.datasets = []; @@ -428,7 +428,7 @@ class SearchController { /** * @type {string} */ - this.color; + this.color = ''; /** * @type {boolean} @@ -436,14 +436,14 @@ class SearchController { this.displayColorPicker = false; /** - * @type {import('ngeo/search/searchDirective.js').SearchDirectiveListeners} + * @type {?import('ngeo/search/searchDirective.js').SearchDirectiveListeners} */ - this.listeners; + this.listeners = null; /** * @type {import('ngeo/search/searchDirective.js').SearchDirectiveListeners} */ - this.additionalListeners; + this.additionalListeners = {}; } @@ -451,6 +451,12 @@ class SearchController { * Called on initialization of the controller. */ $onInit() { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.ngeoLocation_) { + throw new Error('Missing ngeoLocation'); + } const gettextCatalog = this.gettextCatalog_; this.clearButton = this.clearButton !== false; this.colorChooser = this.colorChooser === true; @@ -506,13 +512,13 @@ class SearchController { if (searchQuery) { let resultIndex = 1; if (this.ngeoLocation_.getParam('search-select-index')) { - resultIndex = parseInt(this.ngeoLocation_.getParam('search-select-index'), 10); + resultIndex = parseInt(this.ngeoLocation_.getParam('search-select-index') || '', 10); } let mapZoom; if (this.ngeoLocation_.getParam('search-maxzoom')) { - mapZoom = parseInt(this.ngeoLocation_.getParam('search-maxzoom'), 10); + mapZoom = parseInt(this.ngeoLocation_.getParam('search-maxzoom') || '', 10); } else if (this.ngeoLocation_.getParam('map_zoom')) { - mapZoom = parseInt(this.ngeoLocation_.getParam('map_zoom'), 10); + mapZoom = parseInt(this.ngeoLocation_.getParam('map_zoom') || '', 10); } this.fulltextsearch_(searchQuery, resultIndex, mapZoom); } @@ -558,6 +564,9 @@ class SearchController { * @private */ initDatasets_() { + if (!this.map) { + throw new Error('Missing map'); + } const gettextCatalog = this.gettextCatalog_; for (let i = 0; i < this.datasources.length; i++) { const datasource = this.datasources[i]; @@ -612,7 +621,7 @@ class SearchController { return `
${header}
`; }, suggestion: (suggestion) => { - const coordinates = suggestion['label']; + const coordinates = suggestion.label; let html = `

${coordinates}

`; html = `
${html}
`; @@ -687,7 +696,7 @@ class SearchController { * @return {boolean} */ function(feature) { - const properties = feature.properties; + const properties = feature.properties || {}; if (properties.actions) { // result is an action (add_theme, add_group, ...) // add it to the corresponding group @@ -714,7 +723,7 @@ class SearchController { * @return {boolean} */ function(feature) { - const featureLayerName = feature.properties.layer_name; + const featureLayerName = (feature.properties || {}).layer_name; // Keep only layers with layer_name (don't keep action layers). if (featureLayerName === undefined) { return false; @@ -736,6 +745,9 @@ class SearchController { * @private */ createAndInitBloodhound_(config, opt_filter) { + if (!this.map) { + throw new Error('Missing map'); + } const mapProjectionCode = this.map.getView().getProjection().getCode(); const remoteOptions = this.getBloodhoudRemoteOptions_(); const bloodhound = this.ngeoSearchCreateGeoJSONBloodhound_(config.url, opt_filter, @@ -756,6 +768,9 @@ class SearchController { rateLimitWait: this.delay, prepare: (query, settings) => { const url = settings.url; + if (!url) { + throw new Error('Missing URL'); + } const lang = gettextCatalog.getCurrentLanguage(); settings.xhrFields = { withCredentials: true @@ -845,7 +860,9 @@ class SearchController { * @private */ getSearchStyle_(feature, resolution) { - console.assert(feature); + if (!feature) { + throw new Error('Missing feature'); + } const style = this.styles_[feature.get('layer_name')] || this.styles_['default']; if (this.color) { const color = asColorArray(this.color); @@ -948,6 +965,9 @@ class SearchController { * @private */ select_(event, suggestion, dataset) { + if (!this.map) { + throw new Error('Missing map'); + } if (suggestion.tt_source === 'coordinates') { const geom = new olGeomPoint(suggestion.position); @@ -972,6 +992,9 @@ class SearchController { * @private */ selectFromGMF_(event, feature, dataset) { + if (!this.map) { + throw new Error('Missing map'); + } const actions = feature.get('actions'); const featureGeometry = /** @type {import("ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry()); @@ -1086,6 +1109,9 @@ class SearchController { } this.fullTextSearch_.search(query, {limit: `${resultIndex}`}) .then((data) => { + if (!this.map) { + throw new Error('Missing map'); + } if (data && data.features[resultIndex - 1]) { const format = new olFormatGeoJSON(); const feature = format.readFeature(data.features[resultIndex - 1]); @@ -1093,9 +1119,13 @@ class SearchController { const fitOptions = /** @type {import('ol/View.js').FitOptions} */ ({}); if (opt_zoom !== undefined) { fitOptions.maxZoom = opt_zoom; - fitOptions.size = this.map.getSize(); + fitOptions.size = this.map.getSize() || []; + } + const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); } - this.map.getView().fit(feature.getGeometry().getExtent(), fitOptions); + this.map.getView().fit(geometry.getExtent(), fitOptions); this.inputValue = /** @type {string} */ (feature.get('label')); } }); diff --git a/contribs/gmf/src/theme/Themes.js b/contribs/gmf/src/theme/Themes.js index afa7c1b0290..09a030dbb5b 100644 --- a/contribs/gmf/src/theme/Themes.js +++ b/contribs/gmf/src/theme/Themes.js @@ -103,7 +103,7 @@ export class ThemesService extends olEventsEventTarget { this.loaded = false; /** - * @type {angular.IPromise} + * @type {?angular.IPromise} * @private */ this.bgLayerPromise_ = null; @@ -158,21 +158,24 @@ export class ThemesService extends olEventsEventTarget { const layerLayerCreationFn = function(ogcServers, gmfLayer) { if (gmfLayer.type === 'WMTS') { const gmfLayerWMTS = /** @type import('gmf/themes.js').GmfLayerWMTS */ (gmfLayer); - console.assert(gmfLayerWMTS.url, 'Layer URL is required'); - return layerHelper.createWMTSLayerFromCapabilitites( + if (!gmfLayerWMTS.url) { + throw 'Layer URL is required'; + } + const layer = layerHelper.createWMTSLayerFromCapabilitites( gmfLayerWMTS.url, gmfLayerWMTS.layer || '', gmfLayerWMTS.matrixSet, gmfLayer.dimensions, - gmfLayerWMTS.metadata['customOpenLayersOptions'] + gmfLayerWMTS.metadata.customOpenLayersOptions ).then(callback.bind(null, gmfLayer)).then(null, (response) => { let message = `Unable to build layer "${gmfLayerWMTS.layer}" ` + `from WMTSCapabilities: ${gmfLayerWMTS.url}\n`; - message += `OpenLayers error is "${response['message']}`; + message += `OpenLayers error is "${response.message}`; console.error(message); // Continue even if some layers have failed loading. return $q.resolve(undefined); }); + return /** @type {angular.IPromise.} */(layer); } else if (gmfLayer.type === 'WMS') { const gmfLayerWMS = /** @type import('gmf/themes.js').GmfLayerWMS */ (gmfLayer); console.assert(gmfLayerWMS.ogcServer, 'An OGC server is required'); @@ -197,10 +200,10 @@ export class ThemesService extends olEventsEventTarget { undefined, // time opt_params, server.credential ? 'use-credentials' : 'anonymous', - gmfLayerWMS.metadata['customOpenLayersOptions'] + gmfLayerWMS.metadata.customOpenLayersOptions )); } - console.assert(false, `Unsupported type: ${gmfLayer.type}`); + throw `Unsupported type: ${gmfLayer.type}`; }; /** @@ -208,11 +211,12 @@ export class ThemesService extends olEventsEventTarget { * @param {import('gmf/themes.js').GmfGroup} item The item. * @return {angular.IPromise.} the created layer. */ - const layerGroupCreationFn = function(ogcServers, item) { + const layerGroupCreationFn = (ogcServers, item) => { // We assume no child is a layer group. // The order of insertion in OL3 is the contrary of the theme const orderedChildren = item.children.map(x => x).reverse(); - const promises = orderedChildren.map(layerLayerCreationFn.bind(null, ogcServers)); + const promises = orderedChildren.map((item) => layerLayerCreationFn( + ogcServers, /** @type {import('gmf/themes.js').GmfLayer} */(item))); return $q.all(promises).then((layers) => { let collection; if (layers) { @@ -228,7 +232,7 @@ export class ThemesService extends olEventsEventTarget { /** * @param {import('gmf/themes.js').GmfThemesResponse} data The "themes" web service * response. - * @return {angular.IPromise.>} Promise. + * @return {angular.IPromise>} Promise. */ const promiseSuccessFn = (data) => { const promises = data.background_layers.map((item) => { @@ -244,7 +248,7 @@ export class ThemesService extends olEventsEventTarget { return undefined; } }, this); - return $q.all(promises); + return $q.all(/** @type {Array} */(promises)); }; this.bgLayerPromise_ = this.promise_.then(promiseSuccessFn).then((values) => { @@ -275,43 +279,41 @@ export class ThemesService extends olEventsEventTarget { /** * Get a theme object by its name. * @param {string} themeName Theme name. - * @return {angular.IPromise.} Promise. + * @return {angular.IPromise} Promise. */ getThemeObject(themeName) { return this.promise_.then( /** - * @param {import('gmf/themes.js').GmfThemesResponse} data The "themes" web service - * response. - * @return {import('gmf/themes.js').GmfTheme?} The theme object for themeName, or null - * if not found. + * @param {import('gmf/themes.js').GmfThemesResponse} data The "themes" web service response. + * @return {?import('gmf/themes.js').GmfTheme} The theme object for themeName, or null if not found. */ data => findThemeByName(data.themes, themeName)); } /** * Get an array of theme objects. - * @return {angular.IPromise.>} Promise. + * @return {angular.IPromise>} Promise. */ getThemesObject() { return this.promise_.then( /** - * @param {!import('gmf/themes.js').GmfThemesResponse} data The "themes" web service response. - * @return {!Array.} The themes object. + * @param {import('gmf/themes.js').GmfThemesResponse} data The "themes" web service response. + * @return {Array} The themes object. */ data => data.themes); } /** * Get an array of background layer objects. - * @return {angular.IPromise.>} + * @return {angular.IPromise.>} * Promise. */ getBackgroundLayersObject() { console.assert(this.promise_ !== null); return this.promise_.then( /** - * @param {!import('gmf/themes.js').GmfThemesResponse} data The "themes" web service response. - * @return {!Array.} + * @param {import('gmf/themes.js').GmfThemesResponse} data The "themes" web service response. + * @return {Array} * The background layers object. */ data => data.background_layers @@ -377,7 +379,9 @@ export class ThemesService extends olEventsEventTarget { * Load themes from the "themes" service. */ loadThemes(opt_roleId) { - console.assert(this.treeUrl_, 'gmfTreeUrl should be defined.'); + if (!this.treeUrl_) { + throw 'gmfTreeUrl should be defined.'; + } if (this.loaded) { // reload the themes @@ -421,7 +425,7 @@ export class ThemesService extends olEventsEventTarget { /** * @param {Array.} themes Array of "theme" objects. * @param {string} name The layer name. - * @return {import('gmf/themes.js').GmfGroup} The group. + * @return {?import('gmf/themes.js').GmfGroup} The group. * @hidden */ export function findGroupByLayerNodeName(themes, name) { @@ -443,7 +447,7 @@ export function findGroupByLayerNodeName(themes, name) { * Find a layer group object by its name. Return null if not found. * @param {Array.} themes Array of "theme" objects. * @param {string} name The group name. - * @return {import('gmf/themes.js').GmfGroup} The group. + * @return {?import('gmf/themes.js').GmfGroup} The group. * @hidden */ export function findGroupByName(themes, name) { @@ -464,9 +468,9 @@ export function findGroupByName(themes, name) { /** * Find an object by its name. Return null if not found. - * @param {Array.} objects Array of objects with a 'name' attribute. + * @param {Array} objects Array of objects with a 'name' attribute. * @param {string} objectName The object name. - * @return {T} The object or null. + * @return {?T} The object or null. * @template T * @hidden */ @@ -479,7 +483,7 @@ export function findObjectByName(objects, objectName) { * Find a theme object by its name. Return null if not found. * @param {Array.} themes Array of "theme" objects. * @param {string} themeName The theme name. - * @return {import('gmf/themes.js').GmfTheme} The theme object or null. + * @return {?import('gmf/themes.js').GmfTheme} The theme object or null. * @hidden */ export function findThemeByName(themes, themeName) { diff --git a/contribs/gmf/src/theme/selectorComponent.js b/contribs/gmf/src/theme/selectorComponent.js index f5229cdb836..a566e49f88d 100644 --- a/contribs/gmf/src/theme/selectorComponent.js +++ b/contribs/gmf/src/theme/selectorComponent.js @@ -28,7 +28,7 @@ module.value('gmfThemeSelectorTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['gmfThemeSelectorTemplateUrl']; + const templateUrl = $attrs.gmfThemeSelectorTemplateUrl; return templateUrl !== undefined ? templateUrl : 'gmf/theme/selectorComponent'; }); @@ -121,17 +121,17 @@ function Controller($scope, gmfThemeManager, gmfThemes) { this.gmfThemes_ = gmfThemes; /** - * @type {Array.} + * @type {Array} */ - this.themes; + this.themes = []; /** * @type {function(import('gmf/themes.js').GmfTheme): boolean|undefined} */ - this.filter; + this.filter = (theme) => undefined; /** - * @type {Array.} + * @type {Array} * @private */ this.listenerKeys_ = []; diff --git a/contribs/gmf/src/themes.js b/contribs/gmf/src/themes.js index f8a0fa2e3ba..5668747bc99 100644 --- a/contribs/gmf/src/themes.js +++ b/contribs/gmf/src/themes.js @@ -78,12 +78,12 @@ * extends GmfBaseNode * @typedef {Object} GmfLayer * @property {number} id (GmfBaseNode) - * @property {!GmfMetaData} metadata (GmfBaseNode) + * @property {GmfMetaData} metadata (GmfBaseNode) * @property {string} name (GmfBaseNode) - * @property {!import('ngeo/datasource/OGC.js').Dimensions} dimensions The dimensions managed by the layer, + * @property {import('ngeo/datasource/OGC.js').Dimensions} dimensions The dimensions managed by the layer, * if the value is null we will take the dimension from the application. * Present only on layer in a mixed group. - * @property {!import('ngeo/datasource/OGC.js').DimensionsFiltersConfig} dimensionsFilters The dimensions + * @property {import('ngeo/datasource/OGC.js').DimensionsFiltersConfig} dimensionsFilters The dimensions * applied by filters on the layer configuration, if the value is null we will take the dimension from * the application. * @property {boolean} [editable] @@ -97,14 +97,14 @@ * extends GmfLayer * @typedef {Object} GmfLayerWMS * @property {number} id (GmfBaseNode) - * @property {!GmfMetaData} metadata (GmfBaseNode) + * @property {GmfMetaData} metadata (GmfBaseNode) * @property {string} name (GmfBaseNode) - * @property {!import('ngeo/datasource/OGC.js').Dimensions} dimensions (GmfLayer) - * @property {!import('ngeo/datasource/OGC.js').DimensionsFiltersConfig} dimensionsFilters (GmfLayer) + * @property {import('ngeo/datasource/OGC.js').Dimensions} dimensions (GmfLayer) + * @property {import('ngeo/datasource/OGC.js').DimensionsFiltersConfig} dimensionsFilters (GmfLayer) * @property {boolean} [editable] (GmfLayer) * @property {string} [style] (GmfLayer) * @property {string} type (GmfLayer) - * @property {!Array.} childLayers + * @property {Array} childLayers * @property {string} layers The comma separated list of WMS layers or groups. * @property {number} maxResolutionHint The max resolution where the layer is visible. * @property {number} minResolutionHint The min resolution where the layer is visible. @@ -190,13 +190,13 @@ * @property {string} [iconUrl] The URL of the icon to display in the layer tree. For WMS and WMTS layers. * @property {string} identifierAttributeField The field used in the 'display query window' as feature title. * For WMS layers. - * @property {boolean} [isChecked=false] Is the layer checked by default. For WMS and WMTS layers. - * @property {boolean} [isExpanded=false] Whether the layer group is expanded by default. For layer groups - * (only). + * @property {boolean|undefined} [isChecked=false] Is the layer checked by default. For WMS and WMTS layers. + * @property {boolean|undefined} [isExpanded=false] Whether the layer group is expanded by default. For + * layer groups (only). * @property {boolean} [printNativeAngle=true] Whether the print should rotate the symbols. For layer groups * (only). - * @property {boolean} [isLegendExpanded=false] Whether the legend is expanded by default. For WMS and - * WMTS layers. + * @property {boolean|undefined} [isLegendExpanded=false] Whether the legend is expanded by default. For WMS + * and WMTS layers. * @property {string} [lastUpdateDateColumn] 'Date' column that will be automatically updated after editing * an element. For WMS layers. * @property {string} [lastUpdateUserColumn] 'User' column that will be automatically updated after editing @@ -228,6 +228,7 @@ * @property {string} [wmsLayers] A corresponding WMS layer for a WMTS layers. Used to query the WMTS layers * and to print it. (See also printLayers and queryLayers metadata for more * granularity). For WMTS Layers. + * @property {Object} [customOpenLayersOptions] The custom OpenLayers WMS layer options. */ diff --git a/contribs/gmf/test/spec/directives/displayquerygrid.spec.js b/contribs/gmf/test/spec/directives/displayquerygrid.spec.js index 75effc1ca72..dd0132b13d7 100644 --- a/contribs/gmf/test/spec/directives/displayquerygrid.spec.js +++ b/contribs/gmf/test/spec/directives/displayquerygrid.spec.js @@ -324,7 +324,7 @@ describe('gmf.query.gridComponent', () => { // grid source 2 const gridSource2 = queryGridController.gridSources['Test 3']; expect(gridSource2).toBeDefined(); - expect(gridSource2.configuration).toBe(null); + expect(gridSource2.configuration).toBe(undefined); }); it('deals with sources that all have too many features', () => { @@ -353,12 +353,12 @@ describe('gmf.query.gridComponent', () => { // grid source 1 const gridSource1 = queryGridController.gridSources['Test 1']; expect(gridSource1).toBeDefined(); - expect(gridSource1.configuration).toBe(null); + expect(gridSource1.configuration).toBe(undefined); // grid source 2 const gridSource2 = queryGridController.gridSources['Test 3']; expect(gridSource2).toBeDefined(); - expect(gridSource2.configuration).toBe(null); + expect(gridSource2.configuration).toBe(undefined); }); it('merges sources', () => { @@ -506,7 +506,7 @@ describe('gmf.query.gridComponent', () => { // merged source const gridSource1 = queryGridController.gridSources['merged_source']; expect(gridSource1).toBeDefined(); - expect(gridSource1.configuration).toBe(null); + expect(gridSource1.configuration).toBe(undefined); expect(gridSource1.source.tooManyResults).toBe(true); expect(gridSource1.source.totalFeatureCount).toBe(353); expect(gridSource1.source.features).toEqual([]); diff --git a/contribs/gmf/test/spec/services/authenticationservice.spec.js b/contribs/gmf/test/spec/services/authenticationservice.spec.js index 8d85dc96be0..bfa06bfad31 100644 --- a/contribs/gmf/test/spec/services/authenticationservice.spec.js +++ b/contribs/gmf/test/spec/services/authenticationservice.spec.js @@ -35,11 +35,11 @@ describe('gmf.authentication.Service', () => { it('emits READY after login status check', () => { const spy = jasmine.createSpy(); - /** @type {import('gmf/authentication/Service.js').AuthenticationEvent} */ - let event; + /** @type {?import('gmf/authentication/Service.js').AuthenticationEvent} */ + let event_ = null; olEvents.listenOnce( gmfAuthentication, 'ready', (evt) => { - event = /** @type {import('gmf/authentication/Service.js').AuthenticationEvent} */(evt); + event_ = /** @type {import('gmf/authentication/Service.js').AuthenticationEvent} */(evt); spy(); } ); @@ -50,20 +50,26 @@ describe('gmf.authentication.Service', () => { $httpBackend.flush(); expect(spy.calls.count()).toBe(1); - expect(event).toBeDefined(); - expect(event.type).toBe('ready'); - expect(event.detail.user.username).toBe(null); + expect(event_).toBeDefined(); + if (!event_) { + throw new Error('Missing event_'); + } + // @ts-ignore: ??? + expect(event_.type).toBe('ready'); + // @ts-ignore: ??? + expect(event_.detail.user.username).toBe(null); }); it('logins successful', () => { const spy = jasmine.createSpy(); - /** @type {import('gmf/authentication/Service.js').AuthenticationEvent} */ - let event; + /** @type {?import('gmf/authentication/Service.js').AuthenticationEvent} */ + let event_ = null; olEvents.listenOnce( gmfAuthentication, 'login', (evt) => { - event = /** @type {import('gmf/authentication/Service.js').AuthenticationEvent} */(evt); + event_ = /** @type {import('gmf/authentication/Service.js').AuthenticationEvent} */(evt); spy(); - }); + } + ); $httpBackend.when('POST', loginUrl).respond({'username': 'user'}); @@ -71,9 +77,14 @@ describe('gmf.authentication.Service', () => { $httpBackend.flush(); expect(spy.calls.count()).toBe(1); - expect(event).toBeDefined(); - expect(event.type).toBe('login'); - expect(event.detail.user.username).toBe('user'); + expect(event_).toBeDefined(); + if (!event_) { + throw new Error('Missing event_'); + } + // @ts-ignore: ??? + expect(event_.type).toBe('login'); + // @ts-ignore: ??? + expect(event_.detail.user.username).toBe('user'); }); it('trys to login with wrong credentials', () => { diff --git a/contribs/gmf/test/spec/services/share.spec.js b/contribs/gmf/test/spec/services/share.spec.js index 10ef324002f..f4413456336 100644 --- a/contribs/gmf/test/spec/services/share.spec.js +++ b/contribs/gmf/test/spec/services/share.spec.js @@ -1,4 +1,7 @@ import angular from 'angular'; +import {PermalinkShareService} from 'gmf/permalink/ShareService'; + + describe('gmf.permalink.ShareService', () => { let $httpBackend; const successResponse = { @@ -12,8 +15,7 @@ describe('gmf.permalink.ShareService', () => { it('Should get a short version of the permalink', () => { let shortenerUrl; - /** @type {!import('gmf/permalink/ShareService.js').PermalinkShareService} */ - let gmfShareService; + let gmfShareService = null; angular.mock.inject((_$httpBackend_, _gmfShareService_, _gmfShortenerCreateUrl_) => { $httpBackend = _$httpBackend_; @@ -21,27 +23,36 @@ describe('gmf.permalink.ShareService', () => { shortenerUrl = _gmfShortenerCreateUrl_; $httpBackend.when('POST', shortenerUrl).respond(successResponse); }); + // @ts-ignore: Don't understand ... + if (!(gmfShareService instanceof PermalinkShareService)) { + throw new Error('Missing gmfShareService'); + } - const permalink = 'htpp://fake/c2c/permalink'; - const params = /** @type {import('gmf/permalink/ShareService.js').ShortenerAPIRequestParams} */ ({ + const permalink = 'http://fake/c2c/permalink'; + const params = { url: permalink - }); + }; $httpBackend.expectPOST(shortenerUrl, $.param(params)); + // @ts-ignore: Ununderstandable issue wisible only on Travis... gmfShareService.getShortUrl(permalink); $httpBackend.flush(); params.email = 'fake@c2c.com'; $httpBackend.expectPOST(shortenerUrl, $.param(params)); + if (!params.email) { + throw new Error('Missing params.email'); + } + // @ts-ignore: Ununderstandable issue wisible only on Travis... gmfShareService.sendShortUrl(permalink, params.email); $httpBackend.flush(); }); it('Should return the permalink if no URL for the shorten service has been provided', () => { - let shortenerUrl; - /** @type {!import('gmf/permalink/ShareService.js').PermalinkShareService} */ - let gmfShareService; + /** @type {?string} */ + let shortenerUrl = null; + let gmfShareService = null; angular.mock.module(($provide) => { $provide.value('gmfShortenerCreateUrl', ''); @@ -53,9 +64,16 @@ describe('gmf.permalink.ShareService', () => { shortenerUrl = _gmfShortenerCreateUrl_; $httpBackend.when('POST', shortenerUrl).respond(successResponse); }); + // @ts-ignore: Don't understand ... + if (!(gmfShareService instanceof PermalinkShareService)) { + throw new Error('Missing gmfShareService'); + } + if (shortenerUrl) { + throw new Error('Missing shortenerUrl'); + } + // @ts-ignore: Ununderstandable issue wisible only on Travis... gmfShareService.getShortUrl(shortenerUrl); $httpBackend.verifyNoOutstandingExpectation(); - }); }); diff --git a/contribs/gmf/test/spec/services/syncLayertreeMap.spec.js b/contribs/gmf/test/spec/services/syncLayertreeMap.spec.js index 8bc7fe0e76e..363703fecf6 100644 --- a/contribs/gmf/test/spec/services/syncLayertreeMap.spec.js +++ b/contribs/gmf/test/spec/services/syncLayertreeMap.spec.js @@ -92,7 +92,7 @@ describe('gmf.layertree.SyncLayertreeMap', () => { const roottreeCtrl = element.scope().layertreeCtrl; const treeGroup = roottreeCtrl.children[0]; // Group 'OSM functions mixed' const treeLeaf = treeGroup.children[0]; // osm scale; - const wmsParamLayers = treeLeaf.layer.getSource().getParams()['LAYERS']; + const wmsParamLayers = treeLeaf.layer.getSource().getParams().LAYERS; expect(treeLeaf.node.name).toEqual(wmsParamLayers); }); @@ -109,7 +109,7 @@ describe('gmf.layertree.SyncLayertreeMap', () => { const roottreeCtrl = element.scope().layertreeCtrl; const treeGroup = roottreeCtrl.children[1]; // Group 'Layers' - const wmsParamLayers = treeGroup.layer.getSource().getParams()['LAYERS']; + const wmsParamLayers = treeGroup.layer.getSource().getParams().LAYERS; const checkedLayers = ['cinema', 'police', 'post_office', 'entertainment', 'sustenance', 'hospitals']; // order count ! @@ -158,7 +158,7 @@ describe('gmf.layertree.SyncLayertreeMap', () => { gmfSyncLayertreeMap_.sync_(treeGroup); treeLeaf.setState('on'); gmfSyncLayertreeMap_.sync_(treeLeaf); - const wmsParamLayers = treeLeaf.layer.getSource().getParams()['LAYERS']; + const wmsParamLayers = treeLeaf.layer.getSource().getParams().LAYERS; expect(wmsParamLayers).toBe('osm_scale'); }); @@ -182,13 +182,13 @@ describe('gmf.layertree.SyncLayertreeMap', () => { treeLeaf.setState('on'); gmfSyncLayertreeMap_.sync_(treeLeaf); - let wmsParamLayers = treeGroup.layer.getSource().getParams()['LAYERS']; + let wmsParamLayers = treeGroup.layer.getSource().getParams().LAYERS; expect(wmsParamLayers).toBe('cinema'); // Group is on, original order must be kept. treeGroup.setState('on'); gmfSyncLayertreeMap_.sync_(treeGroup); - wmsParamLayers = treeGroup.layer.getSource().getParams()['LAYERS']; + wmsParamLayers = treeGroup.layer.getSource().getParams().LAYERS; expect(wmsParamLayers).toEqual('hospitals,sustenance,entertainment,' + 'osm_time,post_office,police,cinema'); }); diff --git a/examples/backgroundlayer.js b/examples/backgroundlayer.js index 084d1ddc7bf..adc622a8674 100644 --- a/examples/backgroundlayer.js +++ b/examples/backgroundlayer.js @@ -59,9 +59,9 @@ module.component('appBackgroundlayer', backgroundlayerComponent); function BackgroundlayerController($http, ngeoBackgroundLayerMgr) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {Array.|undefined} @@ -76,7 +76,10 @@ function BackgroundlayerController($http, ngeoBackgroundLayerMgr) { $http.get('data/backgroundlayers.json').then( (resp) => { this.bgLayers = resp.data; - // use the first layer by default + if (!this.bgLayers) { + throw new Error('Missing bgLayers'); + } + // Use the first layer by default this.bgLayer = this.bgLayers[0]; }); @@ -94,8 +97,11 @@ function BackgroundlayerController($http, ngeoBackgroundLayerMgr) { * it. */ BackgroundlayerController.prototype.change = function() { + if (!this.map) { + throw new Error('Missing map'); + } const layerSpec = this.bgLayer; - const layer = this.getLayer_(layerSpec['name']); + const layer = this.getLayer_(layerSpec.name); this.backgroundLayerMgr_.set(this.map, layer); }; @@ -140,17 +146,19 @@ function MainController($scope) { }) }); + const source = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: 'https://wms.geo.admin.ch', + params: {'LAYERS': 'ch.swisstopo.dreiecksvermaschung'}, + serverType: 'mapserver' + }); /** * An overlay layer. * @type {import("ol/layer/Image.js").default} */ + // @ts-ignore: OL issue const overlay = new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: 'https://wms.geo.admin.ch', - params: {'LAYERS': 'ch.swisstopo.dreiecksvermaschung'}, - serverType: 'mapserver' - }) + source }); this.map.addLayer(overlay); diff --git a/examples/backgroundlayerdropdown.js b/examples/backgroundlayerdropdown.js index 071d3b12f73..bfe2910e19c 100644 --- a/examples/backgroundlayerdropdown.js +++ b/examples/backgroundlayerdropdown.js @@ -55,10 +55,15 @@ function BackgroundlayerController($http, ngeoBackgroundLayerMgr) { $http.get('data/backgroundlayers.json').then( (resp) => { const bgLayers = resp.data; - this['bgLayers'] = bgLayers; + this.bgLayers = bgLayers; this.setLayer(bgLayers[0]); }); + /** + * @type {?import("ol/Map.js").default} + */ + this.map = null; + /** * @type {import("ngeo/map/BackgroundLayerMgr.js").MapBackgroundLayerManager} * @private @@ -73,9 +78,12 @@ function BackgroundlayerController($http, ngeoBackgroundLayerMgr) { * @param {Object} layerSpec Layer specification object. */ BackgroundlayerController.prototype.setLayer = function(layerSpec) { - this['currentBgLayer'] = layerSpec; - const layer = this.createLayer_(layerSpec['name']); - this.backgroundLayerMgr_.set(this['map'], layer); + if (!this.map) { + throw new Error('Missing map'); + } + this.currentBgLayer = layerSpec; + const layer = this.createLayer_(layerSpec.name); + this.backgroundLayerMgr_.set(this.map, layer); }; @@ -118,19 +126,21 @@ function MainController($scope) { zoom: 1 }) }); - this['map'] = map; + this.map = map; + const source = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: 'https://wms.geo.admin.ch', + params: {'LAYERS': 'ch.swisstopo.dreiecksvermaschung'}, + serverType: 'mapserver' + }); /** * An overlay layer. * @type {import("ol/layer/Image.js").default} */ + // @ts-ignore: OL issue const overlay = new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: 'https://wms.geo.admin.ch', - params: {'LAYERS': 'ch.swisstopo.dreiecksvermaschung'}, - serverType: 'mapserver' - }) + source }); map.addLayer(overlay); diff --git a/examples/bboxquery.js b/examples/bboxquery.js index d7352a9d3c8..ed524a07219 100644 --- a/examples/bboxquery.js +++ b/examples/bboxquery.js @@ -84,20 +84,24 @@ function MainController($scope, ngeoDataSources) { */ this.queryActive = true; + const source1 = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: MAPSERVER_PROXY, + params: {'LAYERS': 'information'} + }); + // @ts-ignore: OL issue const informationLayer = new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: MAPSERVER_PROXY, - params: {'LAYERS': 'information'} - }) + source: source1 }); + const source2 = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: MAPSERVER_PROXY, + params: {'LAYERS': 'bus_stop'} + }); + // @ts-ignore: OL issue const busStopLayer = new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: MAPSERVER_PROXY, - params: {'LAYERS': 'bus_stop'} - }) + source: source2 }); /** diff --git a/examples/datepicker.js b/examples/datepicker.js index 9828c5497e8..4e7dcb223c8 100644 --- a/examples/datepicker.js +++ b/examples/datepicker.js @@ -34,8 +34,6 @@ function MainController(ngeoTime) { widget: TimePropertyWidgetEnum.DATEPICKER, maxValue: '2013-12-31T00:00:00Z', minValue: '2006-01-01T00:00:00Z', - maxDefValue: null, - minDefValue: null, mode: TimePropertyModeEnum.RANGE, interval: [0, 1, 0, 0] }; @@ -47,8 +45,6 @@ function MainController(ngeoTime) { widget: TimePropertyWidgetEnum.DATEPICKER, maxValue: '2015-12-31T00:00:00Z', minValue: '2014-01-01T00:00:00Z', - maxDefValue: null, - minDefValue: null, mode: TimePropertyModeEnum.VALUE, interval: [0, 1, 0, 0] }; @@ -56,12 +52,12 @@ function MainController(ngeoTime) { /** * @type {string} */ - this.value; + this.value = ''; /** * @type {string} */ - this.rangeValue; + this.rangeValue = ''; this.onDateSelected = function(date) { this.value = date; diff --git a/examples/displaywindow.js b/examples/displaywindow.js index 096f6eb6bf0..7dca8070997 100644 --- a/examples/displaywindow.js +++ b/examples/displaywindow.js @@ -68,10 +68,14 @@ function MainController($scope) { */ this.window4IsOpen = false; + const element = document.getElementById('window4Template'); + if (!element) { + throw new Error('Missing element'); + } /** * @type {string} */ - this.window4Template = angular.element(document.getElementById('window4Template')).html(); + this.window4Template = angular.element(element).html(); /** * @type {string} diff --git a/examples/elevationProfile.js b/examples/elevationProfile.js index d3f67e57110..df6a01b3fb5 100644 --- a/examples/elevationProfile.js +++ b/examples/elevationProfile.js @@ -38,26 +38,28 @@ function MainController($http, $scope) { this.scope_ = $scope; const source = new olSourceVector(); + const source2 = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: 'http://wms.geo.admin.ch/', + crossOrigin: 'anonymous', + attributions: '© ' + + 'Pixelmap 1:500000 / geo.admin.ch', + params: { + 'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale', + 'FORMAT': 'image/jpeg' + }, + serverType: /** @type {import("ol/source/WMSServerType.js").default} */ ('mapserver') + }); /** * @type {import("ol/Map.js").default} */ this.map = new olMap({ layers: [ + // @ts-ignore: OL issue new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: 'http://wms.geo.admin.ch/', - crossOrigin: 'anonymous', - attributions: '© ' + - 'Pixelmap 1:500000 / geo.admin.ch', - params: { - 'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale', - 'FORMAT': 'image/jpeg' - }, - serverType: /** @type {import("ol/source/WMSServerType.js").default} */ ('mapserver') - }) + source: source2 }), new olLayerVector({ source @@ -98,7 +100,7 @@ function MainController($http, $scope) { this.profileData = undefined; $http.get('data/profile.json').then((resp) => { - const data = resp.data['profile']; + const data = resp.data.profile; this.profileData = data; let i; @@ -121,7 +123,11 @@ function MainController($http, $scope) { return; } const coordinate = map.getEventCoordinate(evt.originalEvent); - this.snapToGeometry(coordinate, source.getFeatures()[0].getGeometry()); + const geometry = source.getFeatures()[0].getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + this.snapToGeometry(coordinate, geometry); }); @@ -189,9 +195,9 @@ function MainController($http, $scope) { */ z: (item, opt_z) => { if (opt_z !== undefined) { - item['z'] = opt_z; + item.z = opt_z; } - return item['z']; + return item.z; } }; @@ -206,7 +212,7 @@ function MainController($http, $scope) { const outCallback = () => { this.point = null; - this.snappedPoint_.setGeometry(null); + this.snappedPoint_.setGeometry(undefined); }; @@ -235,15 +241,22 @@ function MainController($http, $scope) { /** * @param {import("ol/coordinate.js").Coordinate} coordinate The current pointer coordinate. - * @param {import("ol/geom/Geometry.js").default|undefined} geometry The geometry to snap to. + * @param {import("ol/geom/Geometry.js").default} geometry The geometry to snap to. */ MainController.prototype.snapToGeometry = function(coordinate, geometry) { + if (!this.map) { + throw new Error('Missing map'); + } const closestPoint = geometry.getClosestPoint(coordinate); // compute distance to line in pixels const dx = closestPoint[0] - coordinate[0]; const dy = closestPoint[1] - coordinate[1]; const dist = Math.sqrt(dx * dx + dy * dy); - const pixelDist = dist / this.map.getView().getResolution(); + const resolution = this.map.getView().getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } + const pixelDist = dist / resolution; if (pixelDist < 8) { this.profileHighlight = closestPoint[2]; diff --git a/examples/layertree.js b/examples/layertree.js index 976eed3339f..ad69966dc92 100644 --- a/examples/layertree.js +++ b/examples/layertree.js @@ -122,7 +122,7 @@ LayertreeController.prototype.getLayer = function(node) { * @param {import("ol/layer/Layer.js").default} layer Layer. */ LayertreeController.prototype.onButtonClick = function(node, layer) { - const layerType = node['layerType']; + const layerType = node.layerType; if (!(layerType in this.promises_)) { this.promises_[layerType] = this.http_.get('data/metadata.html').then( (resp) => { @@ -133,7 +133,7 @@ LayertreeController.prototype.onButtonClick = function(node, layer) { } const infoPopup = this.infoPopup_; this.promises_[layerType].then((html) => { - infoPopup.setTitle(node['name']); + infoPopup.setTitle(node.name); infoPopup.setContent(html); infoPopup.setOpen(true); }); @@ -159,14 +159,14 @@ const getLayer = (function() { const layerCache = {}; return ( /** - * @param {Object} node Tree node. - * @return {import("ol/layer/Layer.js").default} Layer. - */ + * @param {Object} node Tree node. + * @return {?import("ol/layer/Layer.js").default} Layer. + */ function(node) { if (!('layerType' in node)) { return null; } - const type = node['layerType']; + const type = node.layerType; if (type in layerCache) { return layerCache[type]; } diff --git a/examples/locationsearch.js b/examples/locationsearch.js index 36e6e87c884..aed9ee95f9b 100644 --- a/examples/locationsearch.js +++ b/examples/locationsearch.js @@ -48,9 +48,9 @@ appmodule.component('appLocationSearch', locationSearchComponent); function SearchController(ngeoCreateLocationSearchBloodhound) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; const limit = 10; /** @type {Bloodhound} */ @@ -89,7 +89,19 @@ function SearchController(ngeoCreateLocationSearchBloodhound) { * @type {import('ngeo/search/searchDirective.js').SearchDirectiveListeners} */ this.listeners = /** @type {import('ngeo/search/searchDirective.js').SearchDirectiveListeners} */ ({ - select: select_.bind(this) + select: (event, suggestion, dataset) => { + if (!this.map) { + throw new Error('Missing map'); + } + const feature = /** @type {import("ol/Feature.js").default} */ (suggestion); + const bbox = /** @type {import("ol/extent.js").Extent} */ (feature.get('bbox')); + const size = this.map.getSize(); + if (!size) { + throw new Error('issing size'); + } + const maxZoom = 16; + this.map.getView().fit(bbox, {size, maxZoom}); + } }); } @@ -121,22 +133,6 @@ SearchController.prototype.createAndInitBloodhound_ = function(ngeoCreateLocatio }; -/** - * @param {JQueryEventObject} event Event. - * @param {Object} suggestion Suggestion. - * @param {Twitter.Typeahead.Dataset} dataset Dataset. - * @this {SearchController} - */ -function select_(event, suggestion, dataset) { - const feature = /** @type {import("ol/Feature.js").default} */ (suggestion); - const bbox = /** @type {import("ol/extent.js").Extent} */ (feature.get('bbox')); - const size = this.map.getSize(); - console.assert(size !== undefined); - const maxZoom = 16; - this.map.getView().fit(bbox, {size, maxZoom}); -} - - appmodule.controller('AppSearchController', SearchController); diff --git a/examples/mapfishprint.js b/examples/mapfishprint.js index c69d9515e83..d62885b7d8e 100644 --- a/examples/mapfishprint.js +++ b/examples/mapfishprint.js @@ -69,20 +69,22 @@ const PRINT_PAPER_SIZE_ = [555, 675]; * @hidden */ function MainController($timeout, ngeoCreatePrint, ngeoPrintUtils) { + const source = new olSourceImageWMS({ + url: MAPSERVER_PROXY, + projection: undefined, // should be removed in next OL version + params: { + 'LAYERS': 'osm' + }, + serverType: /** @type {import("ol/source/WMSServerType.js").default} */ ('mapserver') + }); /** * @type {import("ol/Map.js").default} */ this.map = new olMap({ layers: [ + // @ts-ignore: OL issue new olLayerImage({ - source: new olSourceImageWMS({ - url: MAPSERVER_PROXY, - projection: undefined, // should be removed in next OL version - params: { - 'LAYERS': 'osm' - }, - serverType: /** @type {import("ol/source/WMSServerType.js").default} */ ('mapserver') - }) + source }), new olLayerVector({ source: new olSourceVector({ @@ -126,7 +128,7 @@ function MainController($timeout, ngeoCreatePrint, ngeoPrintUtils) { this.printUtils_ = ngeoPrintUtils; /** - * @type {function(import("ol/render/Event.js").default)} + * @type {function(import("ol/render/Event.js").default): void} */ const postcomposeListener = ngeoPrintUtils.createPrintMaskPostcompose( /** diff --git a/examples/mapquery.js b/examples/mapquery.js index 09a4378b478..52ae5f3b3db 100644 --- a/examples/mapquery.js +++ b/examples/mapquery.js @@ -96,20 +96,24 @@ function MainController($scope, ngeoDataSources, ngeoToolActivateMgr) { */ this.queryActive = true; + const source1 = new olSourceImageWMS({ + url: MAPSERVER_PROXY, + projection: undefined, // should be removed in next OL version + params: {'LAYERS': 'bus_stop'} + }); + // @ts-ignore: OL issue const busStopLayer = new olLayerImage({ - source: new olSourceImageWMS({ - url: MAPSERVER_PROXY, - projection: undefined, // should be removed in next OL version - params: {'LAYERS': 'bus_stop'} - }) + source: source1 }); + const source2 = new olSourceImageWMS({ + url: MAPSERVER_PROXY, + projection: undefined, // should be removed in next OL version + params: {'LAYERS': 'information'} + }); + // @ts-ignore: OL issue const informationLayer = new olLayerImage({ - source: new olSourceImageWMS({ - url: MAPSERVER_PROXY, - projection: undefined, // should be removed in next OL version - params: {'LAYERS': 'information'} - }) + source: source2 }); /** diff --git a/examples/measure.js b/examples/measure.js index 5707814ba66..f120209e215 100644 --- a/examples/measure.js +++ b/examples/measure.js @@ -70,14 +70,14 @@ function MeasuretoolsController($scope, $compile, $sce, $filter, gettextCatalog) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {string} */ - this.lang; + this.lang = ''; /** * @type {Object} @@ -215,6 +215,9 @@ function MeasuretoolsController($scope, $compile, $sce, module.controller('AppMeasuretoolsController', MeasuretoolsController); MeasuretoolsController.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(this.measureLength); this.map.addInteraction(this.measureArea); this.map.addInteraction(this.measureAzimut); diff --git a/examples/permalink.js b/examples/permalink.js index f891cef5786..7fe285b1291 100644 --- a/examples/permalink.js +++ b/examples/permalink.js @@ -57,9 +57,9 @@ module.component('appMap', mapComponent); */ function MapComponentController(ngeoLocation, ngeoDebounce) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {import("ngeo/statemanager/Location.js").StatemanagerLocation} @@ -77,6 +77,9 @@ function MapComponentController(ngeoLocation, ngeoDebounce) { module.controller('AppMapController', MapComponentController); MapComponentController.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } const view = this.map.getView(); const zoom_ = this.ngeoLocation_.getParam('z'); @@ -103,6 +106,9 @@ MapComponentController.prototype.$onInit = function() { */ (e) => { const center = view.getCenter(); + if (!center) { + throw new Error('Missing center'); + } const params = { 'z': `${view.getZoom()}`, 'x': `${Math.round(center[0])}`, @@ -143,14 +149,14 @@ module.component('appDraw', drawComponent); function DrawComponentController($scope, ngeoLocation) { /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {import("ol/layer/Vector.js").default} + * @type {?import("ol/layer/Vector.js").default} */ - this.layer; + this.layer = null; /** * @type {!import("ngeo/statemanager/Location.js").StatemanagerLocation} @@ -171,12 +177,18 @@ function DrawComponentController($scope, ngeoLocation) { this.featureSeq_ = 0; /** - * @type {import("ol/interaction/Draw.js").default} + * @type {?import("ol/interaction/Draw.js").default} */ - this.interaction; + this.interaction = null; } DrawComponentController.prototype.$onInit = function() { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.layer) { + throw new Error('Missing layer'); + } const vectorSource = /** @type {olSourceVector} */(this.layer.getSource()); this.interaction = new olInteractionDraw({ @@ -224,7 +236,14 @@ DrawComponentController.prototype.$onInit = function() { * Clear the vector layer. */ DrawComponentController.prototype.clearLayer = function() { - /** @type {olSourceVector} */(this.layer.getSource()).clear(true); + if (!this.layer) { + throw new Error('Missing layer'); + } + const source = this.layer.getSource(); + if (!(source instanceof olSourceVector)) { + throw new Error('Wrong source'); + } + source.clear(true); this.featureSeq_ = 0; this.ngeoLocation_.deleteParam('features'); }; diff --git a/examples/search.js b/examples/search.js index 44c9810f6d2..d0e459923a5 100644 --- a/examples/search.js +++ b/examples/search.js @@ -61,9 +61,9 @@ function SearchController($element, $rootScope, $compile, ngeoSearchCreateGeoJSO /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {import("ol/layer/Vector.js").default} @@ -118,11 +118,17 @@ function SearchController($element, $rootScope, $compile, ngeoSearchCreateGeoJSO */ this.listeners = /** @type {import('ngeo/search/searchDirective.js').SearchDirectiveListeners} */ ({ select: (event, suggestion, dataset) => { + if (!this.map) { + throw new Error('Missing map'); + } const feature = /** @type {import('ol/Feature.js').default} */ (suggestion); const featureGeometry = /** @type {import('ol/geom/SimpleGeometry.js').default} */( feature.getGeometry() ); const size = this.map.getSize(); + if (!size) { + throw new Error('Missing size'); + } const source = /** @type {olSourceVector} */(this.vectorLayer_.getSource()); source.clear(true); source.addFeature(feature); @@ -151,6 +157,9 @@ SearchController.prototype.$onInit = function() { * @private */ SearchController.prototype.createVectorLayer_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } const vectorLayer = new olLayerVector({ source: new olSourceVector() }); diff --git a/examples/toolActivate.js b/examples/toolActivate.js index 52e29af92f7..5e4d3f4e8a1 100644 --- a/examples/toolActivate.js +++ b/examples/toolActivate.js @@ -86,6 +86,9 @@ function MainController(ngeoFeatureOverlayMgr, ngeoToolActivateMgr) { // manage clicks on the map this.mapClickIsEnabled = true; const content = document.getElementById('popup-content'); + if (!content) { + throw new Error('Missing content'); + } this.map.on('singleclick', (evt) => { if (this.mapClickIsEnabled) { const c = olCoordinate.toStringXY(evt.coordinate); diff --git a/src/Menu.js b/src/Menu.js index a8955ed8f49..15e72abadd0 100644 --- a/src/Menu.js +++ b/src/Menu.js @@ -38,6 +38,9 @@ export default class extends olOverlay { */ constructor(menuOptions, options = {}) { options.positioning = olOverlayPositioning.TOP_LEFT; + if (!menuOptions) { + throw new Error('Missing menuOptions'); + } super(options); @@ -61,8 +64,7 @@ export default class extends olOverlay { * @type {boolean} * @private */ - this.autoClose_ = menuOptions.autoClose !== undefined ? - menuOptions.autoClose : true; + this.autoClose_ = menuOptions.autoClose !== undefined ? menuOptions.autoClose : true; // titleEl if (menuOptions.title) { @@ -150,6 +152,9 @@ export default class extends olOverlay { */ open(coordinate) { this.setPosition(coordinate); + if (!(document.documentElement instanceof EventTarget)) { + throw new Error('Wrong document element type'); + } if (this.autoClose_) { this.clickOutListenerKey_ = olEvents.listen( document.documentElement, diff --git a/src/Popover.js b/src/Popover.js index 7c27311ec5d..bc52849ce3f 100644 --- a/src/Popover.js +++ b/src/Popover.js @@ -44,6 +44,9 @@ export default class extends olOverlay { */ setMap(map) { const element = this.getElement(); + if (!element) { + throw new Error('Missing element'); + } const currentMap = this.getMap(); if (currentMap) { diff --git a/src/WFSDescribeFeatureType.js b/src/WFSDescribeFeatureType.js index f255616bb98..7bf99938679 100644 --- a/src/WFSDescribeFeatureType.js +++ b/src/WFSDescribeFeatureType.js @@ -5,7 +5,7 @@ import * as olXml from 'ol/xml.js'; /** * @private * @hidden - * @type {Array.} + * @type {Array} */ const NAMESPACE_URIS_ = [ null, @@ -24,14 +24,15 @@ const NAMESPACE_URIS_ = [ */ /** - * @param {Array} namespaceURIs Namespace URIs. + * @param {Array} namespaceURIs Namespace URIs. * @param {parserStructure} structure Structure. * @return {Object} Namespaced structure. * @private * @hidden */ function makeStructureNS(namespaceURIs, structure) { - return /** @type {parsersStructure} */(/** @type {any} */(olXml.makeStructureNS(namespaceURIs, structure))); + return /** @type {parsersStructure} */(/** @type {any} */(olXml.makeStructureNS( + /** @type {Array} */(namespaceURIs), structure))); } /** @@ -149,19 +150,26 @@ WFSDescribeFeatureType.prototype.readFromNode = function(node) { * @private * @hidden * @param {Element} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {!Object.} Attributes. + * @param {Array<*>} objectStack Object stack. + * @return {Object} Attributes. */ function readElement_(node, objectStack) { - /** @type {!Object.} */ + /** @type {Object} */ const attributes = {}; for (let i = 0, len = node.attributes.length; i < len; i++) { const attribute = node.attributes.item(i); + if (!attribute) { + throw new Error('Missing attribute'); + } attributes[attribute.name] = attribute.value; } if (objectStack.length === 1) { // remove namespace from type - attributes['type'] = attributes['type'].split(':').pop(); + const attribute = attributes.type.split(':').pop(); + if (!attribute) { + throw new Error('Missing attribute'); + } + attributes.type = attribute; } return attributes; } @@ -171,8 +179,8 @@ function readElement_(node, objectStack) { * @private * @hidden * @param {Element} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {!Object.} Object. + * @param {Array<*>} objectStack Object stack. + * @return {Object} Object. */ function readComplexType_(node, objectStack) { const name = node.getAttribute('name'); @@ -182,9 +190,8 @@ function readComplexType_(node, objectStack) { node, objectStack ); // flatten - object['complexContent'] = - object['complexContent']['extension']['sequence']['element']; - return object; + object['complexContent'] = object['complexContent'].extension.sequence.element; + return /** @type {Object} */(object); } diff --git a/src/datasource/DataSource.js b/src/datasource/DataSource.js index 93d60b8efcf..f20312b00b5 100644 --- a/src/datasource/DataSource.js +++ b/src/datasource/DataSource.js @@ -82,9 +82,9 @@ class DataSource { * Note: `attributes` is not using the conventionnal getter/setter due * to: See: https://github.com/google/closure-compiler/issues/1089 * - * @type {?Array} + * @type {Array} */ - this.attributes = options.attributes || null; + this.attributes = options.attributes || []; /** * (Required) The data source id. @@ -136,7 +136,7 @@ class DataSource { } /** - * @param {?Array.} attributes Attributes + * @param {Array} attributes Attributes */ setAttributes(attributes) { this.attributes = attributes; diff --git a/src/datasource/DataSources.js b/src/datasource/DataSources.js index f9bf592f932..c2b60ad032a 100644 --- a/src/datasource/DataSources.js +++ b/src/datasource/DataSources.js @@ -34,7 +34,7 @@ export class DataSource { this.collection_ = new olCollection(); /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} * @private */ this.map_ = null; @@ -87,7 +87,9 @@ export class DataSource { // (2) Sync resolution with existing data sources const resolution = view.getResolution(); - console.assert(typeof resolution == 'number'); + if (typeof resolution != 'number') { + throw new Error('Missing resolution'); + } this.syncDataSourcesToResolution_(resolution); } @@ -111,7 +113,9 @@ export class DataSource { const view = evt.target; if (view instanceof olView) { const resolution = view.getResolution(); - console.assert(typeof resolution == 'number'); + if (typeof resolution != 'number') { + throw new Error('Missing resolution'); + } this.syncDataSourcesToResolution_(resolution); } } @@ -165,7 +169,9 @@ export class DataSource { const dataSource = event.element; if (this.map_) { const resolution = this.map_.getView().getResolution(); - console.assert(typeof resolution == 'number'); + if (typeof resolution != 'number') { + throw new Error('Missing resolution'); + } this.syncDataSourceToResolution_(dataSource, resolution); } } diff --git a/src/datasource/Group.js b/src/datasource/Group.js index e9ec1d5b95d..7c03c26c273 100644 --- a/src/datasource/Group.js +++ b/src/datasource/Group.js @@ -120,8 +120,9 @@ class Group { } } - console.assert(typeof state == 'string'); - + if (typeof state != 'string') { + throw new Error('missing state'); + } return state; } diff --git a/src/datasource/OGC.js b/src/datasource/OGC.js index cba8047bd0c..5e9025e7a6c 100644 --- a/src/datasource/OGC.js +++ b/src/datasource/OGC.js @@ -9,7 +9,7 @@ import olFormatWMSGetFeatureInfo from 'ol/format/WMSGetFeatureInfo.js'; /** * Dimensions definition. - * @typedef {Object.} Dimensions + * @typedef {Object} Dimensions */ @@ -156,8 +156,8 @@ export const WMSInfoFormat = { * @property {TimePropertyWidgetEnum} widget * @property {string} maxValue * @property {string} minValue - * @property {string|null} maxDefValue - * @property {string|null} minDefValue + * @property {string} [maxDefValue] + * @property {string} [minDefValue] * @property {TimePropertyModeEnum} mode * @property {TimePropertyResolutionEnum} [resolution] * @property {Array} [values] @@ -174,7 +174,7 @@ export const WMSInfoFormat = { /** * Active dimensions definition, where the value can't be null. - * @typedef {Object.} DimensionsActive + * @typedef {Object} DimensionsActive */ @@ -441,11 +441,13 @@ class OGC extends ngeoDatasourceDataSource { let wfsFormat = null; if (this.supportsWFS && layers.length) { - let format = undefined; + let format; if (this.wfsOutputFormat_ === WFSOutputFormat.GML3) { format = new olFormatGML3(); } else if (this.wfsOutputFormat_ === WFSOutputFormat.GML2) { format = new olFormatGML2(); + } else { + throw new Error('Unknown GML output version'); } console.assert(format); wfsFormat = new olFormatWFS({ @@ -506,14 +508,17 @@ class OGC extends ngeoDatasourceDataSource { * @return {?TimeRange} Time range value */ get timeRangeValue() { + /** @type {?TimeRange} */ let range = null; const lower = this.timeLowerValue; const upper = this.timeUpperValue; if (lower !== undefined) { range = { - end: upper, start: lower }; + if (upper != undefined) { + range.end = upper; + } } return range; } @@ -713,12 +718,14 @@ class OGC extends ngeoDatasourceDataSource { const config = this.dimensionsConfig || {}; for (const key in config) { - if (config[key] === null) { - if (dimensions[key] !== undefined && dimensions[key] !== null) { - active[key] = dimensions[key]; + const configValue = config[key]; + if (configValue === null) { + const value = dimensions[key]; + if (value !== undefined && value !== null) { + active[key] = value; } } else { - active[key] = config[key]; + active[key] = configValue; } } @@ -883,7 +890,7 @@ class OGC extends ngeoDatasourceDataSource { * @param {number} res Resolution. * @param {boolean} queryableOnly Whether to additionally check if the * OGC layer is queryable as well or not. Defaults to `false`. - * @return {Array.} The OGC layer names that are in range. + * @return {Array} The OGC layer names that are in range. */ getInRangeOGCLayerNames(res, queryableOnly = false) { diff --git a/src/datasource/WMSGroup.js b/src/datasource/WMSGroup.js index e4d6a61bbc5..996c78a1622 100644 --- a/src/datasource/WMSGroup.js +++ b/src/datasource/WMSGroup.js @@ -41,10 +41,10 @@ export default class extends ngeoDatasourceOGCGroup { // === PRIVATE properties === /** - * @type {import("ol/layer/Image.js").default} + * @type {?import("ol/layer/Image.js").default} * @private */ - this.layer_; + this.layer_ = null; /** * @type {!import("ngeo/map/LayerHelper.js").LayerHelper} @@ -105,6 +105,9 @@ export default class extends ngeoDatasourceOGCGroup { * @return {import("ol/layer/Image.js").default} layer */ get layer() { + if (!this.layer_) { + throw new Error('Missing layer'); + } return this.layer_; } diff --git a/src/draw/Controller.js b/src/draw/Controller.js index a38ebf1115d..17f3e244fd5 100644 --- a/src/draw/Controller.js +++ b/src/draw/Controller.js @@ -26,28 +26,24 @@ export class DrawController { /** * @type {boolean} */ - this.active; - - if (this.active === undefined) { - this.active = false; - } + this.active = false; /** * Alternate collection of features in which to push the drawn features. * If not defined, then `ngeoFeatures` is used instead. - * @type {!import("ol/Collection.js").default.|undefined} + * @type {?import("ol/Collection.js").default.} */ - this.features; + this.features = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {boolean} */ - this.showMeasure; + this.showMeasure = false; /** * @type {angular.gettext.gettextCatalog} @@ -81,34 +77,34 @@ export class DrawController { this.interactions_ = []; /** - * @type {import("ol/interaction/Draw.js").default} + * @type {?import("ol/interaction/Draw.js").default} */ - this.drawPoint; + this.drawPoint = null; /** - * @type {import("ngeo/interaction/MeasureLength.js").default} + * @type {?import("ngeo/interaction/MeasureLength.js").default} */ - this.measureLength; + this.measureLength = null; /** - * @type {import("ngeo/interaction/MeasureArea.js").default} + * @type {?import("ngeo/interaction/MeasureArea.js").default} */ - this.measureArea; + this.measureArea = null; /** - * @type {import("ngeo/interaction/MeasureAzimut.js").default} + * @type {?import("ngeo/interaction/MeasureAzimut.js").default} */ - this.measureAzimut; + this.measureAzimut = null; /** - * @type {import("ol/interaction/Draw.js").default} + * @type {?import("ol/interaction/Draw.js").default} */ - this.drawRectangle; + this.drawRectangle = null; /** - * @type {import("ol/interaction/Draw.js").default} + * @type {?import("ol/interaction/Draw.js").default} */ - this.drawText; + this.drawText = null; // Watch the "active" property, and disable the draw interactions @@ -131,6 +127,9 @@ export class DrawController { * @param {import("ol/interaction/Interaction.js").default} interaction Interaction to register. */ registerInteraction(interaction) { + if (!this.map) { + throw new Error('Missing map'); + } this.interactions_.push(interaction); interaction.setActive(false); ngeoMiscDecorateInteraction(interaction); diff --git a/src/draw/point.js b/src/draw/point.js index ac77912488b..5dfd0af745a 100644 --- a/src/draw/point.js +++ b/src/draw/point.js @@ -22,12 +22,15 @@ function drawPointComponent() { restrict: 'A', require: '^^ngeoDrawfeature', /** - * @param {!angular.IScope} $scope Scope. + * @param {angular.IScope} $scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} drawFeatureCtrl Controller. + * @param {angular.IController=} drawFeatureCtrl Controller. */ link: ($scope, element, attrs, drawFeatureCtrl) => { + if (!drawFeatureCtrl) { + throw new Error('Missing drawFeatureCtrl'); + } const drawPoint = new olInteractionDraw({ type: /** @type {import("ol/geom/GeometryType.js").default} */ ('Point') diff --git a/src/draw/rectangle.js b/src/draw/rectangle.js index 14ae4c82de4..76112814909 100644 --- a/src/draw/rectangle.js +++ b/src/draw/rectangle.js @@ -26,9 +26,12 @@ function drawRectangleComponent() { * @param {!angular.IScope} $scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} drawFeatureCtrl Controller. + * @param {angular.IController=} drawFeatureCtrl Controller. */ link: ($scope, element, attrs, drawFeatureCtrl) => { + if (!drawFeatureCtrl) { + throw new Error('Missing drawFeatureCtrl'); + } const drawRectangle = new olInteractionDraw({ type: /** @type {import("ol/geom/GeometryType.js").default} */ ('LineString'), diff --git a/src/draw/text.js b/src/draw/text.js index 829ca59e954..4644fe27c12 100644 --- a/src/draw/text.js +++ b/src/draw/text.js @@ -22,12 +22,15 @@ function drawTextComponent() { restrict: 'A', require: '^^ngeoDrawfeature', /** - * @param {!angular.IScope} $scope Scope. + * @param {angular.IScope} $scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} drawFeatureCtrl Controller. + * @param {angular.IController=} drawFeatureCtrl Controller. */ link: ($scope, element, attrs, drawFeatureCtrl) => { + if (!drawFeatureCtrl) { + throw new Error('Missing drawFeatureCtrl'); + } const drawText = new olInteractionDraw({ type: /** @type {import("ol/geom/GeometryType.js").default} */ ('Point') diff --git a/src/editing/attributesComponent.js b/src/editing/attributesComponent.js index 466145c22ed..d73566a2a13 100644 --- a/src/editing/attributesComponent.js +++ b/src/editing/attributesComponent.js @@ -27,7 +27,7 @@ module.value('ngeoAttributesTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['ngeoAttributesTemplateUrl']; + const templateUrl = $attrs.ngeoAttributesTemplateUrl; return templateUrl !== undefined ? templateUrl : 'ngeo/editing/attributescomponent'; }); @@ -96,7 +96,7 @@ function Controller($scope, ngeoEventHelper) { * The list of attributes to create the form with. * @type {Array.} */ - this.attributes; + this.attributes = []; /** * Whether the fieldset should be disabled or not. @@ -106,16 +106,16 @@ function Controller($scope, ngeoEventHelper) { /** * The feature containing the values. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ - this.feature; + this.feature = null; /** * The properties bound to the form, initialized with the inner properties * of the feature. - * @type {?Object.} + * @type {Object} */ - this.properties; + this.properties = {}; /** * @type {!angular.IScope} @@ -147,6 +147,9 @@ function Controller($scope, ngeoEventHelper) { * Initialise the component. */ Controller.prototype.$onInit = function() { + if (!this.feature) { + throw new Error('Missing feature'); + } this.properties = this.feature.getProperties(); // Listen to the feature inner properties change and apply them to the form @@ -163,6 +166,12 @@ Controller.prototype.$onInit = function() { * @param {string} name Attribute name */ Controller.prototype.handleInputChange = function(name) { + if (!this.properties) { + throw new Error('Missing properties'); + } + if (!this.feature) { + throw new Error('Missing feature'); + } this.updating_ = true; const value = this.properties[name]; this.feature.set(name, value); diff --git a/src/editing/createfeatureComponent.js b/src/editing/createfeatureComponent.js index b9002e101b9..d4213fcfc41 100644 --- a/src/editing/createfeatureComponent.js +++ b/src/editing/createfeatureComponent.js @@ -98,55 +98,55 @@ function Controller(gettextCatalog, $compile, $filter, $scope, $timeout, ngeoEve /** * @type {boolean} */ - this.active; + this.active = false; /** - * @type {import("ol/Collection.js").default.|!import("ol/source/Vector.js").default} + * @type {?import("ol/Collection.js").default.|!import("ol/source/Vector.js").default} */ - this.features; + this.features = null; /** * @type {string} */ - this.geomType; + this.geomType = ''; /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {!angular.gettext.gettextCatalog} + * @type {angular.gettext.gettextCatalog} * @private */ this.gettextCatalog_ = gettextCatalog; /** - * @type {!angular.ICompileService} + * @type {angular.ICompileService} * @private */ this.compile_ = $compile; /** - * @type {!angular.IFilterService} + * @type {angular.IFilterService} * @private */ this.filter_ = $filter; /** - * @type {!angular.IScope} + * @type {angular.IScope} * @private */ this.scope_ = $scope; /** - * @type {!angular.ITimeoutService} + * @type {angular.ITimeoutService} * @private */ this.timeout_ = $timeout; /** - * @type {!import("ngeo/misc/EventHelper.js").EventHelper} + * @type {import("ngeo/misc/EventHelper.js").EventHelper} * @private */ this.ngeoEventHelper_ = ngeoEventHelper; @@ -154,16 +154,19 @@ function Controller(gettextCatalog, $compile, $filter, $scope, $timeout, ngeoEve /** * The draw or measure interaction responsible of drawing the vector feature. * The actual type depends on the geometry type. - * @type {import("ol/interaction/Interaction.js").default} + * @type {?import("ol/interaction/Interaction.js").default} * @private */ - this.interaction_; + this.interaction_ = null; // == Event listeners == $scope.$watch( () => this.active, (newVal) => { + if (!this.interaction_) { + throw new Error('Missing interaction'); + } this.interaction_.setActive(newVal); } ); @@ -223,7 +226,12 @@ Controller.prototype.$onInit = function() { ); } - console.assert(interaction); + if (!interaction) { + throw new Error('Missing interaction'); + } + if (!this.map) { + throw new Error('Missing map'); + } interaction.setActive(this.active); this.interaction_ = interaction; @@ -282,6 +290,9 @@ Controller.prototype.handleDrawEnd_ = function(event) { if (this.features instanceof olCollection) { this.features.push(feature); } else { + if (!this.features) { + throw new Error('Missing features'); + } this.features.addFeature(feature); } }; @@ -292,6 +303,12 @@ Controller.prototype.handleDrawEnd_ = function(event) { */ Controller.prototype.$onDestroy = function() { this.timeout_(() => { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.interaction_) { + throw new Error('Missing interaction'); + } const uid = olUtilGetUid(this); this.ngeoEventHelper_.clearListenerKey(uid); this.interaction_.setActive(false); @@ -300,6 +317,4 @@ Controller.prototype.$onDestroy = function() { }; module.controller('ngeoCreatefeatureController', Controller); - - export default module; diff --git a/src/editing/createregularpolygonfromclickComponent.js b/src/editing/createregularpolygonfromclickComponent.js index 81c38dd0b9c..fcbd8b4821e 100644 --- a/src/editing/createregularpolygonfromclickComponent.js +++ b/src/editing/createregularpolygonfromclickComponent.js @@ -95,49 +95,52 @@ function Controller($scope) { $scope.$watch( () => this.active, (newVal) => { + if (!this.interaction_) { + throw new Error('Missing interaction'); + } this.interaction_.setActive(newVal); } ); /** - * @type {number|undefined} + * @type {?number} */ - this.angle; + this.angle = null; /** - * @type {import("ol/Collection.js").default.} + * @type {?import("ol/Collection.js").default.} */ - this.features; + this.features = null; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {number} */ - this.radius; + this.radius = -1; /** - * @type {number|undefined} + * @type {?number} */ - this.sides; + this.sides = null; // == Other properties == /** - * @type {import("ngeo/interaction/DrawRegularPolygonFromClick.js").default} + * @type {?import("ngeo/interaction/DrawRegularPolygonFromClick.js").default} * @private */ - this.interaction_; + this.interaction_ = null; /** - * @type {import("ol/events.js").EventsKey} + * @type {?import("ol/events.js").EventsKey} * @private */ - this.interactionListenerKey_; + this.interactionListenerKey_ = null; $scope.$on('$destroy', this.handleDestroy_.bind(this)); } @@ -147,12 +150,16 @@ function Controller($scope) { * Initialize the directive. */ Controller.prototype.$onInit = function() { - - this.interaction_ = new ngeoInteractionDrawRegularPolygonFromClick({ - angle: this.angle, + const options = { radius: this.radius, - sides: this.sides - }); + }; + if (this.angle !== undefined) { + options.angle = this.angle; + } + if (this.sides !== undefined) { + options.sides = this.sides; + } + this.interaction_ = new ngeoInteractionDrawRegularPolygonFromClick(options); this.interaction_.setActive(this.active); this.interactionListenerKey_ = olEvents.listen( @@ -162,6 +169,9 @@ Controller.prototype.$onInit = function() { this ); + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(this.interaction_); }; @@ -173,6 +183,9 @@ Controller.prototype.$onInit = function() { * @private */ Controller.prototype.handleDrawEnd_ = function(evt) { + if (!this.features) { + throw new Error('Missing features'); + } // @ts-ignore: evt should be of type {import('ol/interaction/Draw.js').DrawEvent but he is private const feature = new olFeature(evt.feature.getGeometry()); this.features.push(feature); @@ -184,6 +197,15 @@ Controller.prototype.handleDrawEnd_ = function(evt) { * @private */ Controller.prototype.handleDestroy_ = function() { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.interactionListenerKey_) { + throw new Error('Missing interactionListenerKey'); + } + if (!this.interaction_) { + throw new Error('Missing interaction'); + } olEvents.unlistenByKey(this.interactionListenerKey_); this.interaction_.setActive(false); this.map.removeInteraction(this.interaction_); diff --git a/src/editing/exportfeaturesComponent.js b/src/editing/exportfeaturesComponent.js index ac7eb11e57f..9488ebda12d 100644 --- a/src/editing/exportfeaturesComponent.js +++ b/src/editing/exportfeaturesComponent.js @@ -64,10 +64,10 @@ module.directive('ngeoExportfeatures', editingExportFeaturesComponent); function Controller($element, $injector, $scope, ngeoFeatureHelper) { /** - * @type {import("ol/Collection.js").default.} + * @type {?import("ol/Collection.js").default.} * @private */ - this.features; + this.features = null; /** * @type {JQuery} @@ -160,6 +160,9 @@ function Controller($element, $injector, $scope, ngeoFeatureHelper) { * @private */ Controller.prototype.handleElementClick_ = function() { + if (!this.features) { + throw new Error('Missing features'); + } const features = this.features.getArray(); @@ -167,7 +170,11 @@ Controller.prototype.handleElementClick_ = function() { this.featureHelper_.export(features, this.formats_[0]); } else if (features.length === 1) { const feature = features[0]; - const geometryType = feature.getGeometry().getType(); + const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + const geometryType = geometry.getType(); let $item; this.formats_.forEach((format, i) => { $item = this.items_[i]; @@ -191,6 +198,12 @@ Controller.prototype.handleElementClick_ = function() { * @private */ Controller.prototype.handleMenuItemClick_ = function(format, event) { + if (!this.features) { + throw new Error('Missing features'); + } + if (!event.target.parentElement) { + throw new Error('Missing event.target.parentElement'); + } if (!$(event.target.parentElement).hasClass('disabled')) { const features = this.features.getArray(); this.featureHelper_.export(features, format); diff --git a/src/filter/RuleHelper.js b/src/filter/RuleHelper.js index c5f958e7276..173d8a1e9a7 100644 --- a/src/filter/RuleHelper.js +++ b/src/filter/RuleHelper.js @@ -27,7 +27,7 @@ import moment from 'moment'; * service. * * @typedef {Object} CreateFilterOptions - * @property {import('ngeo/datasource/DataSource.js').default} dataSource The data source from which to get + * @property {import('ngeo/datasource/OGC.js').default} dataSource The data source from which to get * the filterRules that will be used to create the OL filter object. * @property {boolean} [incDimensions] Whether to include the dimensions related filters. Default to `true`. * @property {boolean} [incTime] Whether to include the data source's time values in the filter created. The @@ -188,7 +188,7 @@ export class RuleHelper { default: if (isCustom) { rule = new ngeoRuleText({ - text: null, + text: '', name: name, operator: RuleOperatorType.LIKE, operators: [ @@ -200,7 +200,7 @@ export class RuleHelper { }); } else { rule = new ngeoRuleText({ - text: null, + text: '', name: name, operator: RuleOperatorType.LIKE, propertyName: attribute.name @@ -259,36 +259,30 @@ export class RuleHelper { * @return {!import("ngeo/rule/Rule.js").default} A clone rule. */ cloneRule(rule) { - - let clone; - - let expression = rule.getExpression(); - if (expression === null) { - expression = undefined; - } - const isCustom = rule.isCustom; - const lowerBoundary = rule.lowerBoundary !== null ? rule.lowerBoundary : - undefined; - const name = rule.name; - const operator = rule.operator !== null ? rule.operator : undefined; - const operators = rule.operators ? rule.operators.slice(0) : undefined; - const propertyName = rule.propertyName; - const type = rule.type !== null ? rule.type : undefined; - const upperBoundary = rule.upperBoundary !== null ? rule.upperBoundary : - undefined; - const options = { - expression, - isCustom, - lowerBoundary, - name, - operator, - operators, - propertyName, - type, - upperBoundary + isCustom: rule.isCustom, + name: rule.name, + propertyName: rule.propertyName, + type: rule.type, }; + const expression = rule.getExpression(); + if (expression !== null) { + options.expression = expression; + } + if (rule.lowerBoundary !== null) { + options.lowerBoundary = rule.lowerBoundary; + } + if (rule.operator) { + options.operator = rule.operator; + } + if (rule.operators) { + options.operators = rule.operators.slice(0); + } + if (rule.upperBoundary !== null) { + options.upperBoundary = rule.upperBoundary; + } + let clone; if (rule instanceof ngeoRuleDate) { clone = new ngeoRuleDate(options); } else if (rule instanceof ngeoRuleGeometry) { @@ -409,8 +403,7 @@ export class RuleHelper { */ createFilter(options) { - const dataSource = /** @type {import("ngeo/datasource/OGC.js").default} */ (options.dataSource); - //const dataSource = options.dataSource; + const dataSource = options.dataSource; let mainFilter = null; if (options.filter) { @@ -589,6 +582,9 @@ export class RuleHelper { const geometryName = dataSource.geometryName; if (rule instanceof ngeoRuleGeometry) { const geometry = rule.geometry; + if (!geometry) { + throw new Error('Missing geometry'); + } if (operator === rsot.CONTAINS) { filter = olFormatFilter.contains( geometryName, @@ -669,21 +665,25 @@ export class RuleHelper { /** * Create and return an OpenLayers filter object using the available * dimensions filters configuration within the data source. - * @param {import("ngeo/datasource/OGC.js").OGCOptions} dataSource Data source from which to create the + * @param {import("ngeo/datasource/OGC.js").default} dataSource Data source from which to create the * filter. * @return {?import("ol/format/filter/Filter.js").default} Filter * @private */ createDimensionsFilterFromDataSource_(dataSource) { - const config = dataSource.dimensionsFiltersConfig; + const config = dataSource.dimensionsFiltersConfig || {}; const dimensions = dataSource.dimensions; + if (!dimensions) { + throw new Error('Missing dimensions'); + } const conditions = []; for (const key in config) { let value = config[key].value; if (value === null) { - if (dimensions[key] !== undefined && dimensions[key] !== null) { - value = dimensions[key]; + const dimensionValue = dimensions[key]; + if (dimensionValue !== undefined && dimensionValue !== null) { + value = dimensionValue; } } if (value !== null) { diff --git a/src/filter/component.js b/src/filter/component.js index 96b90c8440c..03e997df2a8 100644 --- a/src/filter/component.js +++ b/src/filter/component.js @@ -45,7 +45,7 @@ module.value('ngeoFilterTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['ngeoFilterTemplateUrl']; + const templateUrl = $attrs.ngeoFilterTemplateUrl; return templateUrl !== undefined ? templateUrl : 'ngeo/filter'; }); @@ -104,37 +104,37 @@ class FilterController { /** * @type {boolean} */ - this.aRuleIsActive; + this.aRuleIsActive = false; /** - * @type {Array.} + * @type {Array} */ - this.customRules; + this.customRules = []; /** - * @type {!import("ngeo/datasource/OGC.js").default} + * @type {?import("ngeo/datasource/OGC.js").default} */ - this.datasource; + this.datasource = null; /** - * @type {Array.} + * @type {Array} */ - this.directedRules; + this.directedRules = []; /** - * @type {!import("ngeo/map/FeatureOverlay.js").FeatureOverlay} + * @type {?import("ngeo/map/FeatureOverlay.js").FeatureOverlay} */ - this.featureOverlay; + this.featureOverlay = null; /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {string} */ - this.toolGroup; + this.toolGroup = ''; // === Injected properties === @@ -217,6 +217,9 @@ class FilterController { * lists: geometry and the others. Then, apply the filters to the data source. */ $onInit() { + if (!this.datasource) { + throw new Error('Missing datasource'); + } this.scope_.$watch( () => this.aRuleIsActive, @@ -234,7 +237,8 @@ class FilterController { } // (2) All rules that have geometry are added in the featureOverlay - const rules = [].concat(this.customRules, this.directedRules); + const rules_ = []; + const rules = rules_.concat(this.customRules, this.directedRules); for (const rule of rules) { this.registerRule_(rule); } @@ -251,6 +255,12 @@ class FilterController { * Clear the feature overlay. */ $onDestroy() { + if (!this.datasource) { + throw new Error('Missing datasource'); + } + if (!this.featureOverlay) { + throw new Error('Missing featureOverlay'); + } if (this.datasource.filterRules !== null) { this.datasource.filterRules = null; } @@ -262,7 +272,8 @@ class FilterController { * @return {boolean} True if at least one rule is currently defined. */ hasARule() { - return [].concat(this.customRules, this.directedRules).length > 0; + const a = []; + return a.concat(this.customRules, this.directedRules).length > 0; } @@ -271,6 +282,9 @@ class FilterController { * value inside the data source, in the `filterRules` property. */ apply() { + if (!this.datasource) { + throw new Error('Missing datasource'); + } // (1) Reset this.datasource.filterRules = null; @@ -278,6 +292,9 @@ class FilterController { this.timeout_(() => { const filterRules = this.getRulesWithValue_(); if (filterRules.length) { + if (!this.datasource) { + throw new Error('Missing datasource'); + } this.datasource.filterRules = filterRules; // The current query results are cleared when we apply a filter. this.ngeoMapQuerent_.clear(); @@ -291,6 +308,12 @@ class FilterController { * and show the result. */ getData() { + if (!this.datasource) { + throw new Error('Missing datasource'); + } + if (!this.map) { + throw new Error('Missing map'); + } const filterRules = this.getRulesWithValue_(); // No need to do anything if there's no rules. @@ -307,7 +330,9 @@ class FilterController { filterRules: filterRules, srsName: projCode }); - console.assert(filter); + if (!filter) { + throw new Error('Missing filter'); + } this.ngeoMapQuerent_.issue({ dataSources: [dataSource], @@ -325,7 +350,8 @@ class FilterController { */ getRulesWithValue_() { const filterRules = []; - const rules = [].concat(this.customRules, this.directedRules); + const a = []; + const rules = a.concat(this.customRules, this.directedRules); for (const rule of rules) { if (rule.value) { filterRules.push(rule); @@ -357,6 +383,9 @@ class FilterController { * @param {!FilterCondition} condition Condition to set. */ setCondition(condition) { + if (!this.datasource) { + throw new Error('Missing datasource'); + } if (this.datasource.filterCondition !== condition.value) { this.datasource.filterCondition = condition.value; } @@ -380,6 +409,9 @@ class FilterController { * @param {!import("ngeo/rule/Rule.js").default} rule Rule. */ registerRule_(rule) { + if (!this.featureOverlay) { + throw new Error('Missing featureOverlay'); + } const uid = olUtilGetUid(rule); this.ruleUnlisteners_[uid] = this.scope_.$watch( () => rule.active, @@ -395,6 +427,9 @@ class FilterController { * @param {!import("ngeo/rule/Rule.js").default} rule Rule. */ unregisterRule_(rule) { + if (!this.featureOverlay) { + throw new Error('Missing featureOverlay'); + } const uid = olUtilGetUid(rule); const unlistener = this.ruleUnlisteners_[uid]; console.assert(unlistener); @@ -413,7 +448,8 @@ class FilterController { */ handleRuleActiveChange_() { let aRuleIsActive = false; - const rules = [].concat(this.customRules, this.directedRules); + const a = []; + const rules = a.concat(this.customRules, this.directedRules); for (const rule of rules) { if (rule.active) { aRuleIsActive = true; @@ -432,7 +468,8 @@ class FilterController { if (this.aRuleIsActive) { return; } - const rules = [].concat(this.customRules, this.directedRules); + const a = []; + const rules = a.concat(this.customRules, this.directedRules); for (const rule of rules) { if (rule.active) { rule.active = false; diff --git a/src/filter/ruleComponent.js b/src/filter/ruleComponent.js index 2d332f634e7..0eef2140e04 100644 --- a/src/filter/ruleComponent.js +++ b/src/filter/ruleComponent.js @@ -26,6 +26,7 @@ import olStyleStyle from 'ol/style/Style.js'; import olStyleText from 'ol/style/Text.js'; import olStyleFill from 'ol/style/Fill.js'; import {CollectionEvent} from 'ol/Collection.js'; +import Feature from 'ol/Feature.js'; import 'ngeo/sass/font.scss'; @@ -91,13 +92,13 @@ function ngeoRuleTemplateUrl($attrs, ngeoRuleTemplateUrl) { class RuleController { /** - * @param {!angular.gettext.gettextCatalog} gettextCatalog Gettext service. - * @param {!angular.IScope} $scope Angular scope. - * @param {!angular.ITimeoutService} $timeout Angular timeout service. - * @param {!import("ngeo/misc/FeatureHelper.js").FeatureHelper} ngeoFeatureHelper Ngeo feature helper + * @param {angular.gettext.gettextCatalog} gettextCatalog Gettext service. + * @param {angular.IScope} $scope Angular scope. + * @param {angular.ITimeoutService} $timeout Angular timeout service. + * @param {import("ngeo/misc/FeatureHelper.js").FeatureHelper} ngeoFeatureHelper Ngeo feature helper * service. - * @param {!import("ngeo/filter/RuleHelper.js").RuleHelper} ngeoRuleHelper Ngeo rule helper service. - * @param {!import("ngeo/misc/ToolActivateMgr.js").ToolActivateMgr} ngeoToolActivateMgr Ngeo ToolActivate + * @param {import("ngeo/filter/RuleHelper.js").RuleHelper} ngeoRuleHelper Ngeo rule helper service. + * @param {import("ngeo/misc/ToolActivateMgr.js").ToolActivateMgr} ngeoToolActivateMgr Ngeo ToolActivate * manager service. * @private * @ngInject @@ -110,25 +111,25 @@ class RuleController { // Binding properties /** - * @type {!import("ngeo/map/FeatureOverlay.js").FeatureOverlay} + * @type {?import("ngeo/map/FeatureOverlay.js").FeatureOverlay} */ - this.featureOverlay; + this.featureOverlay = null; /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * The original rule. - * @type {!import("ngeo/rule/Rule.js").default} + * @type {?import("ngeo/rule/Rule.js").default} */ - this.rule; + this.rule = null; /** * @type {string} */ - this.toolGroup; + this.toolGroup = ''; // Injected properties @@ -176,9 +177,9 @@ class RuleController { * The cloned rule. Changes in the UI are applied to the clone 'on-the-fly'. * Changes in the clone are applied back in the original rule when the * apply button is clicked. - * @type {!import("ngeo/rule/Rule.js").default} + * @type {?import("ngeo/rule/Rule.js").default} */ - this.clone; + this.clone = null; const operatorType = RuleOperatorType; const spatialOperatorType = RuleSpatialOperatorType; @@ -225,7 +226,6 @@ class RuleController { /** * Time property used when the rule is of type 'date|datetime' and uses * a range of date. - * @type {!import('ngeo/datasource/OGC.js').TimeProperty} */ this.timeRangeMode = { widget: 'datepicker', @@ -240,7 +240,6 @@ class RuleController { /** * Time property used when the rule is of type 'date|datetime' and uses * a single date. - * @type {!import('ngeo/datasource/OGC.js').TimeProperty} */ this.timeValueMode = { widget: 'datepicker', @@ -253,13 +252,13 @@ class RuleController { }; /** - * @type {!import("ngeo/misc/ToolActivate.js").default} + * @type {?import("ngeo/misc/ToolActivate.js").default} * @private */ - this.toolActivate_;// = new ngeo.misc.ToolActivate(this.rule, 'active'); + this.toolActivate_ = null;// = new ngeo.misc.ToolActivate(this.rule, 'active'); /** - * @type {!Array.} + * @type {Array} * @private */ this.unlisteners_ = []; @@ -273,12 +272,12 @@ class RuleController { this.drawActive = false; /** - * @type {!import("ngeo/misc/ToolActivate.js").default} + * @type {import("ngeo/misc/ToolActivate.js").default} */ this.drawToolActivate = new ngeoMiscToolActivate(this, 'drawActive'); /** - * @type {!import("ol/Collection.js").default.} + * @type {import("ol/Collection.js").default.} */ this.drawnFeatures = new olCollection(); @@ -289,18 +288,18 @@ class RuleController { this.menu_ = null; /** - * @type {!import("ol/Collection.js").default.} + * @type {import("ol/Collection.js").default.} */ this.selectedFeatures = new olCollection(); /** - * @type {!import("ol/Collection.js").default.} + * @type {import("ol/Collection.js").default.} * @private */ this.interactions_ = new olCollection(); /** - * @type {!import("ngeo/interaction/Modify.js").default} + * @type {import("ngeo/interaction/Modify.js").default} * @private */ this.modify_ = new ngeoInteractionModify({ @@ -346,7 +345,7 @@ class RuleController { this.interactions_.push(this.translate_); /** - * @type {!Array.} + * @type {Array} * @private */ this.listenerKeys_ = []; @@ -354,7 +353,7 @@ class RuleController { this.initializeInteractions_(); /** - * @type {!import("ngeo/misc/ToolActivate.js").default} + * @type {import("ngeo/misc/ToolActivate.js").default} */ this.modifyToolActivate = new ngeoMiscToolActivate( this.modify_, @@ -390,6 +389,9 @@ class RuleController { * Clone the rule to be able to work with the clone directly. */ $onInit() { + if (!this.rule) { + throw new Error('Missing rule'); + } this.clone = this.ngeoRuleHelper_.cloneRule(this.rule); this.toolActivate_ = new ngeoMiscToolActivate(this.rule, 'active'); @@ -398,7 +400,13 @@ class RuleController { this.toolGroup, this.toolActivate_); this.scope_.$watch( - () => this.rule.active, + () => { + if (!this.rule) { + throw new Error('Missing rule'); + } + this.rule.active; + return false; + }, this.handleActiveChange_.bind(this) ); @@ -414,27 +422,45 @@ class RuleController { ) { // Watch 'expression' this.unlisteners_.push(this.scope_.$watch( - () => this.clone.getExpression(), + () => { + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.getExpression(); + }, (newVal) => { if (typeof newVal == 'string') { + // @ts-ignore: Why? this.timeValueMode.minDefValue = newVal || this.createDate_(); } } )); // Watch 'lowerBoundary' this.unlisteners_.push(this.scope_.$watch( - () => this.clone.lowerBoundary, + () => { + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.lowerBoundary; + }, (newVal) => { if (typeof newVal == 'string') { + // @ts-ignore: Why? this.timeRangeMode.minDefValue = newVal || this.createWeekAgoDate_(); } } )); // Watch 'upperBoundary' this.unlisteners_.push(this.scope_.$watch( - () => this.clone.upperBoundary, + () => { + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.upperBoundary; + }, (newVal) => { if (typeof newVal == 'string') { + // @ts-ignore: Why? this.timeRangeMode.maxDefValue = newVal || this.createDate_(); } } @@ -445,9 +471,15 @@ class RuleController { // supported by the newly selected operator. If it doesn't, reset // the expression, i.e. geometry. this.unlisteners_.push(this.scope_.$watch( - () => this.clone.operator, + () => { + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.operator; + }, (newVal) => { this.drawActive = false; + // @ts-ignore: Why? if (newVal && newVal === RuleSpatialOperatorType.CONTAINS) { const clone = this.clone; if (clone instanceof ngeoRuleGeometry) { @@ -460,6 +492,9 @@ class RuleController { ngeoGeometryType.RECTANGLE ]; if (!supportedTypes.includes(geomType)) { + if (!this.clone) { + throw new Error('Missing clone'); + } this.clone.setExpression(null); } } @@ -470,8 +505,14 @@ class RuleController { // Watch 'expression' of clone. Set 'geomType' property accordingly. this.unlisteners_.push(this.scope_.$watch( - () => this.clone.expression, + () => { + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.expression; + }, (newVal) => { + // @ts-ignore: Why? if (newVal) { const clone = this.clone; if (clone instanceof ngeoRuleGeometry) { @@ -488,6 +529,12 @@ class RuleController { // selection collection. this.unlisteners_.push(this.scope_.$watch( () => { + if (!this.clone) { + throw new Error('Missing clone'); + } + if (!this.rule) { + throw new Error('Missing rule'); + } const hasExpression = this.clone.getExpression() !== null; const isActive = this.rule.active === true; const editToolIsActive = this.modify_.getActive() || @@ -513,6 +560,15 @@ class RuleController { * Called on destruction of the controller. */ $onDestroy() { + if (!this.clone) { + throw new Error('Missing clone'); + } + if (!this.rule) { + throw new Error('Missing rule'); + } + if (!this.toolActivate_) { + throw new Error('Missing toolActivate'); + } if (this.rule.active) { this.rule.active = false; // in $onDestroy, setting active to false will not call the handler. Call @@ -531,6 +587,9 @@ class RuleController { /** */ toggle() { + if (!this.rule) { + throw new Error('Missing rule'); + } if (this.rule.active) { this.cancel(); } else { @@ -542,6 +601,12 @@ class RuleController { * Apply the changes that were made in the original rule. */ apply() { + if (!this.clone) { + throw new Error('Missing clone'); + } + if (!this.rule) { + throw new Error('Missing rule'); + } this.ngeoRuleHelper_.extendRule(this.clone, this.rule); this.rule.active = false; } @@ -550,6 +615,12 @@ class RuleController { * Revert the changes that were made in the clone rule. */ cancel() { + if (!this.rule) { + throw new Error('Missing rule'); + } + if (!this.clone) { + throw new Error('Missing clone'); + } this.ngeoRuleHelper_.extendRule(this.rule, this.clone); this.rule.active = false; } @@ -558,6 +629,12 @@ class RuleController { * Reset both original and clone rules. */ reset() { + if (!this.rule) { + throw new Error('Missing rule'); + } + if (!this.clone) { + throw new Error('Missing clone'); + } this.clone.reset(); this.rule.reset(); } @@ -568,6 +645,9 @@ class RuleController { * @param {string} choice Choice that has been clicked. */ toggleChoiceSelection(choice) { + if (!this.clone) { + throw new Error('Missing clone'); + } const rule = this.clone; const choices = rule.getExpression() ? /** @type {string} */(rule.getExpression()).split(',') : []; const idx = choices.indexOf(choice); @@ -584,15 +664,21 @@ class RuleController { * @param {Object} date Date */ onDateSelected(date) { - this.clone.setExpression(date['start']); + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.setExpression(date.start); } /** * @param {Object} date Date */ onDateRangeSelected(date) { - this.clone.lowerBoundary = date['start']; - this.clone.upperBoundary = date['end']; + if (!this.clone) { + throw new Error('Missing clone'); + } + this.clone.lowerBoundary = date.start; + this.clone.upperBoundary = date.end; } /** @@ -644,6 +730,12 @@ class RuleController { * @private */ handleActiveChange_(active, oldActive) { + if (!this.map) { + throw new Error('Missing map'); + } + if (!this.featureOverlay) { + throw new Error('Missing featureOverlay'); + } if (!(this.rule instanceof ngeoRuleGeometry) || !(this.clone instanceof ngeoRuleGeometry) || @@ -716,6 +808,7 @@ class RuleController { } } else { + // @ts-ignore: OL issue? cloneFeature.setStyle(null); keys.forEach(olEvents.unlistenByKey); keys.length = 0; @@ -757,6 +850,9 @@ class RuleController { */ registerInteractions_() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.addInteraction(interaction); }); } @@ -767,6 +863,9 @@ class RuleController { */ unregisterInteractions_() { this.interactions_.forEach((interaction) => { + if (!this.map) { + throw new Error('Missing map'); + } this.map.removeInteraction(interaction); }); } @@ -807,13 +906,14 @@ class RuleController { /** * Return the type of geometry used by the rule feature. Used in the template. - * @return {string} Geometry type. + * @return {?string} Geometry type. */ getRuleGeometryType() { const rule = this.rule; if (rule instanceof ngeoRuleGeometry) { return this.ngeoFeatureHelper_.getType(rule.feature); } + return null; } /** @@ -822,6 +922,9 @@ class RuleController { */ handleMapContextMenu_(evt) { if (evt instanceof Event || evt instanceof TouchEvent) { + if (!this.map) { + throw new Error('Missing map'); + } // (1) Remove previous menu, if any this.removeMenu_(); @@ -829,22 +932,24 @@ class RuleController { const pixel = this.map.getEventPixel(evt); const coordinate = this.map.getCoordinateFromPixel(pixel); - let feature = this.map.forEachFeatureAtPixel( + const feature = this.map.forEachFeatureAtPixel( pixel, (feature) => { - /** @type {import('ol/Feature.js').default} */ + /** @type {?Feature} */ let ret = null; - if (this.selectedFeatures.getArray().includes( - /** @type {import('ol/Feature.js').default} */(feature) - )) { - ret = /** @type {import('ol/Feature.js').default} */(feature); + if (!(feature instanceof Feature)) { + throw new Error('Wrong feature type'); + } + if (this.selectedFeatures.getArray().includes(feature)) { + ret = feature; + } + if (!ret) { + throw new Error('Missing feature'); } return ret; } ); - feature = feature ? feature : null; - // (3) If the clicked feature is the selected one, plus if it has a certain // type of geometry, then show the menu const actions = []; @@ -904,6 +1009,9 @@ class RuleController { */ removeMenu_() { if (this.menu_) { + if (!this.map) { + throw new Error('Missing map'); + } olEvents.unlisten( this.menu_, 'actionclick', diff --git a/src/format/Attribute.js b/src/format/Attribute.js index 32eab7a2196..b3b863120bc 100644 --- a/src/format/Attribute.js +++ b/src/format/Attribute.js @@ -25,8 +25,8 @@ import ngeoFormatAttributeType from 'ngeo/format/AttributeType.js'; * @property {string} [type] (AttributeBase) * @property {Array.} [choices] The list of possible values for the attribute. * @property {number} [maxLength] Specifies the maximum number of character for the attribute value. - * @property {string|null} name The attribute name. - * @property {string|null} alias The attribute alias + * @property {string} [name] The attribute name. + * @property {string} [alias] The attribute alias * @property {boolean} [readonly] Whether the attribute's value should be prevented from being edited * or not. Defaults to `false`. * @property {boolean} [required] Whether the attribute is required to have a value set or not. diff --git a/src/format/FeatureHash.js b/src/format/FeatureHash.js index cbfdd306208..d4765d17f1c 100644 --- a/src/format/FeatureHash.js +++ b/src/format/FeatureHash.js @@ -18,6 +18,7 @@ import olStyleFill from 'ol/style/Fill.js'; import olStyleStroke from 'ol/style/Stroke.js'; import olStyleStyle from 'ol/style/Style.js'; import olStyleText from 'ol/style/Text.js'; +import Geometry from 'ol/geom/Geometry.js'; /** @@ -27,7 +28,7 @@ import olStyleText from 'ol/style/Text.js'; * @property {number} [accuracy] The encoding and decoding accuracy. Optional. Default value is 1. * @property {Object.} [defaultValues] defaultValues. * @property {boolean} [encodeStyles=true] Encode styles. Optional. - * @property {function(import("ol/Feature.js").default): Object.} [properties] + * @property {function(import("ol/Feature.js").default): Object.} [properties] * A function that returns serializable properties for a feature. Optional. By default the feature * properties (as returned by `feature.getProperties()`) are used. To be serializable the returned * properties should be numbers or strings. @@ -87,7 +88,7 @@ const CHAR64_ = * @const * @private * @hidden - * @type {Object.} + * @type {Object.} */ const GEOMETRY_READERS_ = { 'P': readMultiPointGeometry_, @@ -103,7 +104,7 @@ const GEOMETRY_READERS_ = { * @const * @private * @hidden - * @type {Object.} + * @type {Object.} */ const GEOMETRY_WRITERS_ = { 'MultiLineString': writeMultiLineStringGeometry_, @@ -140,28 +141,25 @@ class FeatureHash extends olFormatTextFeature { constructor(opt_options) { super(); - const options = opt_options !== undefined ? opt_options : {}; + const options = opt_options || {}; /** * @type {number} * @private */ - this.accuracy_ = options.accuracy !== undefined ? - options.accuracy : DEFAULT_ACCURACY; + this.accuracy_ = options.accuracy || DEFAULT_ACCURACY; /** * @type {boolean} * @private */ - this.encodeStyles_ = options.encodeStyles !== undefined ? - options.encodeStyles : true; + this.encodeStyles_ = options.encodeStyles || true; /** - * @type {function(import("ol/Feature.js").default):Object.} + * @type {function(import("ol/Feature.js").default):Object.} * @private */ - this.propertiesFunction_ = options.properties !== undefined ? - options.properties : defaultPropertiesFunction_; + this.propertiesFunction_ = options.properties || defaultPropertiesFunction_; /** * @type {boolean} @@ -185,13 +183,13 @@ class FeatureHash extends olFormatTextFeature { * @type {Object.} * @private */ - LegacyProperties_ = (options.propertiesType !== undefined) && options.propertiesType; + LegacyProperties_ = options.propertiesType || {}; /** * @type {Object.} * @private */ - this.defaultValues_ = options.defaultValues !== undefined ? options.defaultValues : {}; + this.defaultValues_ = options.defaultValues || {}; } /** @@ -200,8 +198,8 @@ class FeatureHash extends olFormatTextFeature { * two dimensions and in latitude, longitude order. * corresponding to a geometry's coordinates. * @param {string} text Text. - * @param {Array.=} opt_flatCoordinates Flat coordinates array. - * @return {Array.} Flat coordinates. + * @param {Array=} opt_flatCoordinates Flat coordinates array. + * @return {Array} Flat coordinates. * @private */ decodeCoordinates_(text, opt_flatCoordinates) { @@ -240,7 +238,7 @@ class FeatureHash extends olFormatTextFeature { * Encode an array of number (corresponding to some coordinates) into a * logical sequence of characters. The coordinates are assumed to be in * two dimensions and in latitude, longitude order. - * @param {Array.} flatCoordinates Flat coordinates. + * @param {Array} flatCoordinates Flat coordinates. * @param {number} stride Stride. * @param {number} offset Offset. * @param {number} end End. @@ -319,7 +317,7 @@ class FeatureHash extends olFormatTextFeature { * Read multiple features from a logical sequence of characters. * @param {string} text Text. * @param {import('ol/format/Feature.js').ReadOptions=} opt_options Read options. - * @return {Array.} Features. + * @return {Array} Features. * @protected * @override */ @@ -327,7 +325,7 @@ class FeatureHash extends olFormatTextFeature { console.assert(text[0] === 'F'); this.prevX_ = 0; this.prevY_ = 0; - /** @type {Array.} */ + /** @type {Array} */ const features = []; text = text.substring(1); while (text.length > 0) { @@ -355,7 +353,7 @@ class FeatureHash extends olFormatTextFeature { * Read a geometry from a logical sequence of characters. * @param {string} text Text. * @param {import('ol/format/Feature.js').ReadOptions=} opt_options Read options. - * @return {import("ol/geom/Geometry.js").default} Geometry. + * @return {Geometry} Geometry. * @protected * @override */ @@ -374,7 +372,7 @@ class FeatureHash extends olFormatTextFeature { * @override */ writeFeatureText(feature, opt_options) { - const /** @type {Array.} */ encodedParts = []; + const /** @type {Array} */ encodedParts = []; // encode geometry @@ -393,7 +391,7 @@ class FeatureHash extends olFormatTextFeature { // encode properties - const /** @type {Array.} */ encodedProperties = []; + const /** @type {Array} */ encodedProperties = []; const propFunction = this.propertiesFunction_(feature); for (const key in propFunction) { const value = propFunction[key]; @@ -418,12 +416,14 @@ class FeatureHash extends olFormatTextFeature { if (this.encodeStyles_) { const styleFunction = feature.getStyleFunction(); if (styleFunction !== undefined) { - let styles = styleFunction.call(feature, 0); + let styles = styleFunction.call(null, feature, 0); if (styles !== null) { const encodedStyles = []; styles = Array.isArray(styles) ? styles : [styles]; - encodeStyles_( - styles, geometry.getType(), encodedStyles); + if (!geometry) { + throw new Error('Missing geometry'); + } + encodeStyles_(styles, geometry.getType(), encodedStyles); if (encodedStyles.length > 0) { encodedParts.push('~'); Array.prototype.push.apply(encodedParts, encodedStyles); @@ -440,7 +440,7 @@ class FeatureHash extends olFormatTextFeature { /** * Encode an array of features into a logical sequence of characters. - * @param {Array.} features Feature. + * @param {Array} features Feature. * @param {import('ol/format/Feature.js').ReadOptions=} opt_options Read options. * @return {string} Encoded features. * @protected @@ -461,19 +461,24 @@ class FeatureHash extends olFormatTextFeature { /** * Encode a geometry into a logical sequence of characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. + * @param {Geometry} geometry Geometry. * @param {import('ol/format/Feature.js').ReadOptions=} opt_options Read options. * @return {string} Encoded geometry. * @protected * @override */ writeGeometryText(geometry, opt_options) { - const geometryWriter = GEOMETRY_WRITERS_[ - geometry.getType()]; + const geometryWriter = GEOMETRY_WRITERS_[geometry.getType()]; console.assert(geometryWriter !== undefined); - const transformedGeometry = /** @type {import("ol/geom/Geometry.js").default} */ - (olFormatFeature.transformWithOptions(geometry, true, opt_options)); - return geometryWriter.call(this, transformedGeometry); + const transformedGeometry = olFormatFeature.transformWithOptions(geometry, true, opt_options); + if (!(transformedGeometry instanceof Geometry)) { + throw new Error('Missing transformedGeometry'); + } + const encGeom = geometryWriter.call(this, transformedGeometry); + if (!encGeom) { + throw new Error('Missing encodedGeometry'); + } + return encGeom; } } @@ -482,7 +487,7 @@ export default FeatureHash; /** * Get features's properties. * @param {import("ol/Feature.js").default} feature Feature. - * @return {Object.} The feature properties to + * @return {Object.} The feature properties to * serialize. * @private * @hidden @@ -528,9 +533,9 @@ function encodeNumber_(num) { * For a type of geometry, transforms an array of {@link import("ol/style/Style.js").default} into * a logical sequence of characters and put the result into the given encoded * styles's array. - * @param {Array.} styles Styles. + * @param {Array} styles Styles. * @param {import("ol/geom/GeometryType.js").default} geometryType Geometry type. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @private * @hidden */ @@ -567,7 +572,7 @@ function encodeStyles_(styles, geometryType, encodedStyles) { * Transform an {@link import("ol/style/Stroke.js").default} into a logical sequence of * characters and put the result into the given encoded styles's array. * @param {import("ol/style/Stroke.js").default} strokeStyle Stroke style. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @private * @hidden */ @@ -579,7 +584,7 @@ function encodeStyleLine_(strokeStyle, encodedStyles) { * Transform an {@link import("ol/style/Circle.js").default} into a logical sequence of * characters and put the result into the given encoded styles's array. * @param {import("ol/style/Image.js").default} imageStyle Image style. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @private * @hidden */ @@ -607,7 +612,7 @@ function encodeStylePoint_(imageStyle, encodedStyles) { * the given encoded styles's array. * @param {import("ol/style/Fill.js").default} fillStyle Fill style. * @param {import("ol/style/Stroke.js").default} strokeStyle Stroke style. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @private * @hidden */ @@ -623,7 +628,7 @@ function encodeStylePolygon_(fillStyle, strokeStyle, encodedStyles) { * a logical sequence of characters and put the result into the given encoded * styles's array. * @param {import("ol/style/Fill.js").default} fillStyle Fill style. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @param {string=} opt_propertyName Property name. * @private * @hidden @@ -650,7 +655,7 @@ function encodeStyleFill_(fillStyle, encodedStyles, opt_propertyName) { * Transform an {@link import("ol/style/Stroke.js").default} into a logical sequence of * characters and put the result into the given encoded styles's array. * @param {import("ol/style/Stroke.js").default} strokeStyle Stroke style. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @private * @hidden */ @@ -678,7 +683,7 @@ function encodeStyleStroke_(strokeStyle, encodedStyles) { * Transform an {@link import("ol/style/Text.js").default} into a logical sequence of characters and * put the result into the given encoded styles's array. * @param {import("ol/style/Text.js").default} textStyle Text style. - * @param {Array.} encodedStyles Encoded styles array. + * @param {Array} encodedStyles Encoded styles array. * @private * @hidden */ @@ -857,12 +862,12 @@ function setStyleInFeature_(text, feature) { return; } const properties = getStyleProperties_(text, feature); - const fillColor = properties['fillColor']; - const fontSize = properties['fontSize']; - const fontColor = properties['fontColor']; - const pointRadius = properties['pointRadius']; - const strokeColor = properties['strokeColor']; - const strokeWidth = properties['strokeWidth']; + const fillColor = properties.fillColor; + const fontSize = properties.fontSize; + const fontColor = properties.fontColor; + const pointRadius = properties.pointRadius; + const strokeColor = properties.strokeColor; + const strokeWidth = properties.strokeWidth; let fillStyle = null; if (fillColor !== undefined) { @@ -872,18 +877,29 @@ function setStyleInFeature_(text, feature) { } let strokeStyle = null; if (strokeColor !== undefined && strokeWidth !== undefined) { + if (typeof strokeWidth != 'number') { + throw new Error('Missing strokeWidth'); + } strokeStyle = new olStyleStroke({ color: /** @type {Array|string} */ (strokeColor), - width: /** @type {number} */ (strokeWidth) + width: strokeWidth }); } let imageStyle = null; if (pointRadius !== undefined) { - imageStyle = new olStyleCircle({ - radius: /** @type {number} */ (pointRadius), - fill: fillStyle, - stroke: strokeStyle - }); + if (typeof pointRadius != 'number') { + throw new Error('Missing pointRadius'); + } + const options = { + radius: pointRadius, + }; + if (fillStyle) { + options.fill = fillStyle; + } + if (strokeStyle) { + options.stroke = strokeStyle; + } + imageStyle = new olStyleCircle(options); fillStyle = strokeStyle = null; } let textStyle = null; @@ -895,12 +911,20 @@ function setStyleInFeature_(text, feature) { }) }); } - const style = new olStyleStyle({ - fill: fillStyle, - image: imageStyle, - stroke: strokeStyle, - text: textStyle - }); + const options = {}; + if (fillStyle) { + options.fill = fillStyle; + } + if (strokeStyle) { + options.stroke = strokeStyle; + } + if (imageStyle) { + options.image = imageStyle; + } + if (textStyle) { + options.text = textStyle; + } + const style = new olStyleStyle(options); feature.setStyle(style); } @@ -920,32 +944,32 @@ function setStyleProperties_(text, feature) { // Deal with legacy properties if (geometry instanceof olGeomPoint) { - if (properties['isLabel'] || + if (properties.isLabel || properties[ngeoFormatFeatureProperties.IS_TEXT]) { - delete properties['strokeColor']; - delete properties['fillColor']; + delete properties.strokeColor; + delete properties.fillColor; } else { - delete properties['fontColor']; - delete properties['fontSize']; + delete properties.fontColor; + delete properties.fontSize; } } else { - delete properties['fontColor']; + delete properties.fontColor; if (geometry instanceof olGeomLineString) { - delete properties['fillColor']; - delete properties['fillOpacity']; + delete properties.fillColor; + delete properties.fillOpacity; } } // Convert font size from px to pt - if (properties['fontSize']) { - const fontSizeStr = /** @type {string} */(properties['fontSize']); + if (properties.fontSize) { + const fontSizeStr = /** @type {string} */(properties.fontSize); /** @type {number} */ let fontSize = parseFloat(fontSizeStr); if (fontSizeStr.indexOf('px') !== -1) { fontSize = Math.round(fontSize / 1.333333); } - properties['fontSize'] = fontSize; + properties.fontSize = fontSize; } // Convert legacy properties @@ -1009,14 +1033,13 @@ function castValue_(key, value) { * match the geometry of the feature. * @param {string} text Text. * @param {import("ol/Feature.js").default} feature Feature. - * @return {Object.} The style properties for - * the feature. + * @return {Object} The style properties for the feature. * @private * @hidden */ function getStyleProperties_(text, feature) { const parts = text.split('\''); - /** @type {Object.} */ + /** @type {Object} */ const properties = {}; for (let i = 0; i < parts.length; ++i) { @@ -1035,8 +1058,8 @@ function getStyleProperties_(text, feature) { /** * Encode a {@link import("ol/geom/LineString.js").default} geometry into a logical sequence of * characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. - * @return {string} Encoded geometry. + * @param {Geometry} geometry Geometry. + * @return {?string} Encoded geometry. * @private * @hidden * @this {FeatureHash} @@ -1048,13 +1071,14 @@ function writeLineStringGeometry_(geometry) { const end = flatCoordinates.length; return `l(${this.encodeCoordinates_(flatCoordinates, stride, 0, end)})`; } + return null; } /** * Encode a {@link import("ol/geom/MultiLineString.js").default} geometry into a logical sequence * of characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. - * @return {string} Encoded geometry. + * @param {Geometry} geometry Geometry. + * @return {?string} Encoded geometry. * @private * @hidden * @this {FeatureHash} @@ -1079,13 +1103,14 @@ function writeMultiLineStringGeometry_(geometry) { textArray.push(')'); return textArray.join(''); } + return null; } /** * Encode a {@link import("ol/geom/Point.js").default} geometry into a logical sequence of * characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. - * @return {string} Encoded geometry. + * @param {Geometry} geometry Geometry. + * @return {?string} Encoded geometry. * @private * @hidden * @this {FeatureHash} @@ -1097,13 +1122,14 @@ function writePointGeometry_(geometry) { const end = flatCoordinates.length; return `p(${this.encodeCoordinates_(flatCoordinates, stride, 0, end)})`; } + return null; } /** * Encode an {@link import("ol/geom/MultiPoint.js").default} geometry into a logical sequence * of characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. - * @return {string} Encoded geometry. + * @param {Geometry} geometry Geometry. + * @return {?string} Encoded geometry. * @private * @hidden * @this {FeatureHash} @@ -1115,15 +1141,16 @@ function writeMultiPointGeometry_(geometry) { const end = flatCoordinates.length; return `P(${this.encodeCoordinates_(flatCoordinates, stride, 0, end)})`; } + return null; } /** * Helper to encode an {@link import("ol/geom/Polygon.js").default} geometry. - * @param {Array.} flatCoordinates Flat coordinates. + * @param {Array} flatCoordinates Flat coordinates. * @param {number} stride Stride. * @param {number} offset Offset. - * @param {Array.} ends Ends. - * @param {Array.} textArray Text array. + * @param {Array} ends Ends. + * @param {Array} textArray Text array. * @return {number} The new offset. * @private * @hidden @@ -1147,8 +1174,8 @@ function encodeRings_(flatCoordinates, stride, offset, ends, textArray) { /** * Encode an {@link import("ol/geom/Polygon.js").default} geometry into a logical sequence * of characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. - * @return {string} Encoded geometry. + * @param {Geometry} geometry Geometry. + * @return {?string} Encoded geometry. * @private * @hidden * @this {FeatureHash} @@ -1165,13 +1192,14 @@ function writePolygonGeometry_(geometry) { textArray.push(')'); return textArray.join(''); } + return null; } /** * Encode an {@link import("ol/geom/MultiPoligon.js").default} geometry into a logical sequence of * characters. - * @param {import("ol/geom/Geometry.js").default} geometry Geometry. + * @param {Geometry} geometry Geometry. * @return {string} Encoded geometry. * @private * @hidden @@ -1194,6 +1222,6 @@ function writeMultiPolygonGeometry_(geometry) { } return textArray.join(''); } else { - console.assert('Wrong geometry type'); + throw new Error('Wrong geometry type'); } } diff --git a/src/format/WFSAttribute.js b/src/format/WFSAttribute.js index 1378f8917ec..eed5838b8be 100644 --- a/src/format/WFSAttribute.js +++ b/src/format/WFSAttribute.js @@ -32,19 +32,18 @@ export default class { */ readFromComplexTypeElement_(object) { - const name = object['name']; - const alias = 'alias' in object ? object['alias'] : null; - const required = object['minOccurs'] != '0'; + const name = object.name; + const alias = object.alias || null; + const required = object.minOccurs != '0'; /** @type {import('ngeo/format/Attribute.js').Attribute} */ const attribute = { - type: null, name, alias, required }; - const type = object['type']; + const type = object.type; if (!setGeometryType(attribute, type)) { if (type === 'gml:TimeInstantType' || type === 'dateTime') { diff --git a/src/format/XSDAttribute.js b/src/format/XSDAttribute.js index 91fe3e030bc..7790bdde61f 100644 --- a/src/format/XSDAttribute.js +++ b/src/format/XSDAttribute.js @@ -30,13 +30,13 @@ class XSDAttribute extends olFormatXML { } /** - * @param {Document|Node|string} source Source. + * @param {Document|Element|string} source Source. * @return {Array.} The parsed result. * @override */ read(source) { return ( - /** @type {Array.} */ olFormatXML.prototype.read.call( + /** @type {Array} */ olFormatXML.prototype.read.call( this, source ) ); @@ -45,14 +45,21 @@ class XSDAttribute extends olFormatXML { /** * @param {Document} doc Document. - * @return {Array.} List of attributes. + * @return {?Array} List of attributes. * @override */ readFromDocument(doc) { console.assert(doc.nodeType == Node.DOCUMENT_NODE, 'doc.nodeType should be DOCUMENT'); - for (let n = /** @type {Node} */(doc.firstChild); n; n = n.nextSibling) { + /** + * @type {?ChildNode} + */ + let n; + for (n = doc.firstChild; n; n = /** @type {ChildNode} */(n.nextSibling)) { if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(/** @type {Element} */(n)); + if (!(n instanceof Element)) { + throw new Error('Wrong type'); + } + return this.readFromNode(n); } } return null; @@ -94,7 +101,9 @@ class XSDAttribute extends olFormatXML { const elementNode = /** @type {Element} */(node); const name = elementNode.getAttribute('name'); - console.assert(typeof name == 'string', 'name should be defined in element node.'); + if (!name) { + throw new Error('name should be defined in element node'); + } const alias = elementNode.getAttribute('alias'); const nillable = elementNode.getAttribute('nillable'); @@ -107,12 +116,13 @@ class XSDAttribute extends olFormatXML { /** @type {import('ngeo/format/Attribute.js').Attribute} */ const attribute = { - type: null, name, - alias, readonly, required }; + if (alias) { + attribute.alias = alias; + } const type = elementNode.getAttribute('type'); if (type) { @@ -132,7 +142,11 @@ class XSDAttribute extends olFormatXML { attribute.type = ngeoFormatAttributeType.SELECT; const choices = []; for (let i = 0, ii = enumerations.length; i < ii; i++) { - choices.push(enumerations[i].getAttribute('value')); + const value = enumerations[i].getAttribute('value'); + if (!value) { + throw new Error('Missing value'); + } + choices.push(value); } attribute.choices = choices; } else { @@ -143,10 +157,11 @@ class XSDAttribute extends olFormatXML { } if (restrictions.length && restrictions[0]) { const restrictionNode = restrictions[0]; - this.setAttributeByXsdType_( - attribute, - restrictionNode.getAttribute('base') - ); + const base = restrictionNode.getAttribute('base'); + if (!base) { + throw new Error('Missing base'); + } + this.setAttributeByXsdType_(attribute, base); // MaxLength let maxLengths = elementNode.getElementsByTagName('maxLength'); if (!maxLengths.length) { diff --git a/src/geolocation/desktop.js b/src/geolocation/desktop.js index 101e3268378..cddcea09702 100644 --- a/src/geolocation/desktop.js +++ b/src/geolocation/desktop.js @@ -174,7 +174,11 @@ function Controller($scope, $element, ngeoFeatureOverlayMgr, ngeoNotification) { this.active_ = false; olEvents.listen(this.geolocation_, 'change:accuracyGeometry', () => { - this.accuracyFeature_.setGeometry(this.geolocation_.getAccuracyGeometry()); + const geometry = this.geolocation_.getAccuracyGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + this.accuracyFeature_.setGeometry(geometry); }); olEvents.listen(this.geolocation_, 'change:position', (evt) => { diff --git a/src/geolocation/mobile.js b/src/geolocation/mobile.js index 78f0f582285..4a7b106907c 100644 --- a/src/geolocation/mobile.js +++ b/src/geolocation/mobile.js @@ -206,7 +206,11 @@ function Controller($scope, $element, gettextCatalog, ngeoFeatureOverlayMgr, nge this.viewChangedByMe_ = false; olEvents.listen(this.geolocation_, 'change:accuracyGeometry', () => { - this.accuracyFeature_.setGeometry(this.geolocation_.getAccuracyGeometry()); + const geometry = this.geolocation_.getAccuracyGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + this.accuracyFeature_.setGeometry(geometry); this.setPosition_(); }); @@ -239,6 +243,9 @@ Controller.prototype.toggleTracking = function() { console.assert(currentPosition !== undefined); // stop tracking if the position is close to the center of the map. const center = this.map_.getView().getCenter(); + if (!center) { + throw new Error('Missing center'); + } const diff = Math.abs(currentPosition[0] - center[0]) + Math.abs(currentPosition[1] - center[1]); if (diff < 2) { this.untrack_(); @@ -313,8 +320,13 @@ Controller.prototype.handleViewChange_ = function(event) { Controller.prototype.autorotateListener = function() { let currentAlpha = 0; if (window.hasOwnProperty('ondeviceorientationabsolute')) { - window.addEventListener('deviceorientationabsolute', (evt) => { - const event = /** @type {DeviceOrientationEvent} */(evt); + window.addEventListener('deviceorientationabsolute', (event) => { + if (!(event instanceof DeviceOrientationEvent)) { + throw new Error('Wrong event type'); + } + if (!event.alpha) { + throw new Error('Missing event.alpha'); + } currentAlpha = this.handleRotate_(event.alpha, currentAlpha); }, true); } else if (window.hasOwnProperty('ondeviceorientation')) { @@ -324,6 +336,9 @@ Controller.prototype.autorotateListener = function() { // @ts-ignore: ios only currentAlpha = this.handleRotate_(-evt.webkitCompassHeading, currentAlpha); } else { // non iOS + if (!evt.alpha) { + throw new Error('Missing evt.alpha'); + } currentAlpha = this.handleRotate_(evt.alpha - 270, currentAlpha); } }, true); diff --git a/src/googlestreetview/component.js b/src/googlestreetview/component.js index 57ef00c6629..be7c8b70c95 100644 --- a/src/googlestreetview/component.js +++ b/src/googlestreetview/component.js @@ -25,7 +25,7 @@ module.value('ngeoGooglestreetviewTemplateUrl', * @return {string} The template url. */ ($attrs) => { - const templateUrl = $attrs['ngeoGooglestreetviewTemplateUrl']; + const templateUrl = $attrs.ngeoGooglestreetviewTemplateUrl; return templateUrl !== undefined ? templateUrl : 'ngeo/googlestreetview'; }); @@ -77,7 +77,7 @@ class GoogleStreetviewController { /** * @type {boolean} */ - this.active; + this.active = false; $scope.$watch( () => this.active, @@ -86,25 +86,25 @@ class GoogleStreetviewController { /** * Style for the feature. - * @type {import("ol/style/Style.js").StyleLike|undefined} + * @type {?import("ol/style/Style.js").StyleLike} */ - this.featureStyle; + this.featureStyle = null; /** - * @type {!import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** - * @type {number|undefined} + * @type {number} */ - this.radius; + this.radius = -1; // Injected properties /** - * @type {!angular.IScope} + * @type {angular.IScope} * @private */ this.scope_ = $scope; @@ -252,6 +252,9 @@ class GoogleStreetviewController { * @private */ handleActiveChange_(active) { + if (!this.map) { + throw new Error('Missing map'); + } const keys = this.listenerKeys_; @@ -342,7 +345,12 @@ class GoogleStreetviewController { * @private */ handleStreetViewServiceGetPanorama_(data, status) { - + if (!data.location) { + throw new Error('Missing data.location'); + } + if (!data.location.latLng) { + throw new Error('Missing data.location.latLng'); + } const panorama = this.panorama_; if (status === google.maps.StreetViewStatus.OK) { @@ -377,10 +385,10 @@ class GoogleStreetviewController { * @return {import("ol/coordinate.js").Coordinate} Map view projection coordinate. */ fromLonLat_(lonLat) { - return olProj.fromLonLat( - lonLat, - this.map.getView().getProjection() - ); + if (!this.map) { + throw new Error('Missing map'); + } + return olProj.fromLonLat(lonLat, this.map.getView().getProjection()); } /** @@ -388,10 +396,10 @@ class GoogleStreetviewController { * @return {import("ol/coordinate.js").Coordinate} LonLat coordinate. */ toLonLat_(coordinate) { - return olProj.toLonLat( - coordinate, - this.map.getView().getProjection() - ); + if (!this.map) { + throw new Error('Missing map'); + } + return olProj.toLonLat(coordinate, this.map.getView().getProjection()); } } diff --git a/src/grid/Config.js b/src/grid/Config.js index 4f7160ce025..47d85a14c23 100644 --- a/src/grid/Config.js +++ b/src/grid/Config.js @@ -61,6 +61,9 @@ GridConfig.prototype.getSelectedCount = function() { * @return {Array.} Selected rows in the current ordering. */ GridConfig.prototype.getSelectedRows = function() { + if (!this.data) { + throw new Error('Missing data'); + } return this.data.filter(row => this.isRowSelected(row)); }; @@ -94,6 +97,9 @@ GridConfig.prototype.toggleRow = function(attributes) { * Select all rows. */ GridConfig.prototype.selectAll = function() { + if (!this.data) { + throw new Error('Missing data'); + } this.data.forEach((attributes) => { this.selectRow(attributes); }); @@ -114,6 +120,9 @@ GridConfig.prototype.unselectAll = function() { * Invert selection. */ GridConfig.prototype.invertSelection = function() { + if (!this.data) { + throw new Error('Missing data'); + } this.data.forEach((attributes) => { this.toggleRow(attributes); }); diff --git a/src/grid/component.js b/src/grid/component.js index 4555aae76b4..08baaf5a940 100644 --- a/src/grid/component.js +++ b/src/grid/component.js @@ -30,7 +30,7 @@ module.value('ngeoGridTemplateUrl', * @return {string} Template URL. */ ($attrs) => { - const templateUrl = $attrs['ngeoGridTemplateurl']; + const templateUrl = $attrs.ngeoGridTemplateurl; return templateUrl !== undefined ? templateUrl : 'ngeo/grid'; } @@ -95,20 +95,20 @@ function Controller($scope) { this.scope_ = $scope; /** - * @type {import("ngeo/grid/Config.js").default} + * @type {?import("ngeo/grid/Config.js").default} */ - this.configuration; + this.configuration = null; /** - * @type {Object.} + * @type {Object} */ - this.selectedRows; + this.selectedRows = {}; /** * The name of the column used to sort the grid. * @type {string} */ - this.sortedBy; + this.sortedBy = ''; /** * @type {boolean} @@ -131,6 +131,9 @@ function Controller($scope) { * Init the controller */ Controller.prototype.$onInit = function() { + if (!this.configuration) { + throw new Error('Missing configuration'); + } this.selectedRows = this.configuration.selectedRows; }; @@ -143,6 +146,12 @@ Controller.prototype.$onInit = function() { * sort the data. */ Controller.prototype.sort = function(columnName) { + if (!this.configuration) { + throw new Error('Missing configuration'); + } + if (!this.configuration.data) { + throw new Error('Missing configuration.data'); + } this.sortAscending = this.sortedBy === columnName ? !this.sortAscending : true; this.sortedBy = columnName; @@ -178,8 +187,10 @@ Controller.prototype.clickRow = function(attributes, event) { * @param {boolean} platformModifierKey CTRL/Meta pressed? * @private */ -Controller.prototype.clickRow_ = function( - attributes, shiftKey, platformModifierKey) { +Controller.prototype.clickRow_ = function(attributes, shiftKey, platformModifierKey) { + if (!this.configuration) { + throw new Error('Missing configuration'); + } if (shiftKey && !platformModifierKey) { this.selectRange_(attributes); @@ -201,6 +212,12 @@ Controller.prototype.clickRow_ = function( * @private */ Controller.prototype.selectRange_ = function(attributes) { + if (!this.configuration) { + throw new Error('Missing configuration'); + } + if (!this.configuration.data) { + throw new Error('Missing configuration.data'); + } const targetUid = getRowUid(attributes); const data = this.configuration.data; @@ -209,8 +226,8 @@ Controller.prototype.selectRange_ = function(attributes) { } // get the position of the clicked and all already selected rows - /** @type {number|undefined} */ - let posClickedRow = undefined; + /** @type {number} */ + let posClickedRow = 0; const posSelectedRows = []; for (let i = 0; i < data.length; i++) { const currentRow = data[i]; @@ -222,7 +239,6 @@ Controller.prototype.selectRange_ = function(attributes) { posSelectedRows.push(i); } } - console.assert(posClickedRow !== undefined); if (posSelectedRows.length == 0) { // if no other row is selected, select the clicked one and stop diff --git a/src/interaction/DrawAzimut.js b/src/interaction/DrawAzimut.js index dfe2b5793a6..12c78d34736 100644 --- a/src/interaction/DrawAzimut.js +++ b/src/interaction/DrawAzimut.js @@ -10,7 +10,8 @@ import olGeomPoint from 'ol/geom/Point.js'; import olInteractionPointer from 'ol/interaction/Draw.js'; import olLayerVector from 'ol/layer/Vector.js'; import olSourceVector from 'ol/source/Vector.js'; - +import VectorSource from 'ol/source/Vector.js'; +import Feature from 'ol/Feature.js'; /** * @typedef {Object} Options @@ -30,7 +31,7 @@ class DrawAzimut extends olInteractionPointer { */ constructor(options) { super({ - type: undefined + type: '', }); this.shouldStopEvent = FALSE; @@ -39,14 +40,14 @@ class DrawAzimut extends olInteractionPointer { * @type {import("ol/pixel.js").Pixel} * @private */ - this.downPx_ = null; + this.downPx_ = []; /** * Target source for drawn features. * @type {import("ol/source/Vector.js").default} * @private */ - this.source_ = options.source !== undefined ? options.source : null; + this.source_ = options.source; /** * Tglls whether the drawing has started or not. @@ -57,17 +58,17 @@ class DrawAzimut extends olInteractionPointer { /** * Sketch feature. - * @type {import("ol/Feature.js").default} + * @type {Feature} * @private */ - this.sketchFeature_ = null; + this.sketchFeature_ = new Feature(); /** * Sketch point. - * @type {import("ol/Feature.js").default} + * @type {Feature} * @private */ - this.sketchPoint_ = null; + this.sketchPoint_ = new Feature(); /** @@ -117,7 +118,7 @@ class DrawAzimut extends olInteractionPointer { */ createOrUpdateSketchPoint_(event) { const coordinates = event.coordinate.slice(); - if (this.sketchPoint_ === null) { + if (this.sketchPoint_.getGeometry() === null) { this.sketchPoint_ = new olFeature(new olGeomPoint(coordinates)); this.updateSketchFeatures_(); } else { @@ -134,12 +135,8 @@ class DrawAzimut extends olInteractionPointer { */ updateSketchFeatures_() { const sketchFeatures = []; - if (this.sketchFeature_ !== null) { - sketchFeatures.push(this.sketchFeature_); - } - if (this.sketchPoint_ !== null) { - sketchFeatures.push(this.sketchPoint_); - } + sketchFeatures.push(this.sketchFeature_); + sketchFeatures.push(this.sketchPoint_); const source = /** @type {olSourceVector} */(this.sketchLayer_.getSource()); source.clear(true); source.addFeatures(sketchFeatures); @@ -172,9 +169,10 @@ class DrawAzimut extends olInteractionPointer { */ modifyDrawing_(event) { const coordinate = event.coordinate; - const geometry = /** @type {import('ol/geom/GeometryCollection.js').default} */( - this.sketchFeature_.getGeometry() - ); + const geometry = this.sketchFeature_.getGeometry(); + if (!(geometry instanceof olGeomGeometryCollection)) { + throw new Error('Missing geometry'); + } const geometries = geometry.getGeometriesArray(); const line = geometries[0]; if (line instanceof olGeomLineString) { @@ -198,17 +196,19 @@ class DrawAzimut extends olInteractionPointer { /** * Stop drawing without adding the sketch feature to the target layer. - * @return {import("ol/Feature.js").default} The sketch feature (or null if none). + * @return {Feature} The sketch feature (or null if none). * @private */ abortDrawing_() { this.started_ = false; const sketchFeature = this.sketchFeature_; - if (sketchFeature !== null) { - this.sketchFeature_ = null; - this.sketchPoint_ = null; - /** @type {olSourceVector} */(this.sketchLayer_.getSource()).clear(true); + this.sketchFeature_ = new Feature(); + this.sketchPoint_ = new Feature(); + const source = this.sketchLayer_.getSource(); + if (!(source instanceof VectorSource)) { + throw new Error('Missing source'); } + source.clear(true); return sketchFeature; } @@ -221,6 +221,7 @@ class DrawAzimut extends olInteractionPointer { if (map === null || !active) { this.abortDrawing_(); } + // @ts-ignore: OL issue this.sketchLayer_.setMap(active ? map : null); } @@ -230,7 +231,6 @@ class DrawAzimut extends olInteractionPointer { */ finishDrawing_() { const sketchFeature = this.abortDrawing_(); - console.assert(sketchFeature !== null); if (this.source_ !== null) { this.source_.addFeature(sketchFeature); @@ -250,8 +250,7 @@ class DrawAzimut extends olInteractionPointer { } /** - * @param {import("ol/MapBrowserPointerEvent.js").default} event Event. - * @return {boolean} Start drag sequence? + * @inheritDoc */ handleDownEvent(event) { this.downPx_ = event.pixel; @@ -259,10 +258,12 @@ class DrawAzimut extends olInteractionPointer { } /** - * @param {import("ol/MapBrowserPointerEvent.js").default} event Event. - * @return {boolean} Stop drag sequence? + * @inheritDoc */ handleUpEvent(event) { + if (!this.downPx_) { + throw new Error('Missing downPx'); + } const downPx = this.downPx_; const clickPx = event.pixel; const dx = downPx[0] - clickPx[0]; @@ -282,8 +283,7 @@ class DrawAzimut extends olInteractionPointer { } /** - * @param {import("ol/MapBrowserEvent.js").default} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. + * @inheritDoc */ handleEvent(mapBrowserEvent) { let pass = true; diff --git a/src/interaction/Measure.js b/src/interaction/Measure.js index 700df170362..65784d4bcaa 100644 --- a/src/interaction/Measure.js +++ b/src/interaction/Measure.js @@ -12,6 +12,7 @@ import olSourceVector from 'ol/source/Vector.js'; import olStyleFill from 'ol/style/Fill.js'; import olStyleStroke from 'ol/style/Stroke.js'; import olStyleStyle from 'ol/style/Style.js'; +import VectorSource from 'ol/source/Vector.js'; /** @@ -59,14 +60,14 @@ class Measure extends olInteractionInteraction { /** * The help tooltip element. - * @type {HTMLElement} + * @type {?HTMLElement} * @private */ this.helpTooltipElement_ = null; /** * Overlay to show the help messages. - * @type {import("ol/Overlay.js").default} + * @type {?import("ol/Overlay.js").default} * @private */ this.helpTooltipOverlay_ = null; @@ -74,7 +75,7 @@ class Measure extends olInteractionInteraction { /** * The measure tooltip element. - * @type {HTMLElement} + * @type {?HTMLElement} * @private */ this.measureTooltipElement_ = null; @@ -82,7 +83,7 @@ class Measure extends olInteractionInteraction { /** * Overlay to show the measurement. - * @type {import("ol/Overlay.js").default} + * @type {?import("ol/Overlay.js").default} * @private */ this.measureTooltipOverlay_ = null; @@ -90,7 +91,7 @@ class Measure extends olInteractionInteraction { /** * The measurement overlay coordinate. - * @type {import("ol/coordinate.js").Coordinate} + * @type {?import("ol/coordinate.js").Coordinate} * @private */ this.measureTooltipOverlayCoord_ = null; @@ -98,7 +99,7 @@ class Measure extends olInteractionInteraction { /** * The sketch feature. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} * @protected */ this.sketchFeature = null; @@ -132,17 +133,18 @@ class Measure extends olInteractionInteraction { */ this.displayHelpTooltip_ = options.displayHelpTooltip !== undefined ? options.displayHelpTooltip : true; + let startMsg; + if (options.startMsg !== undefined) { + startMsg = options.startMsg; + } else { + startMsg = document.createElement('span'); + startMsg.textContent = 'Click to start drawing.'; + } /** * The message to show when user is about to start drawing. * @type {Element} */ - this.startMsg; - if (options.startMsg !== undefined) { - this.startMsg = options.startMsg; - } else { - this.startMsg = document.createElement('span'); - this.startMsg.textContent = 'Click to start drawing.'; - } + this.startMsg = startMsg; /** * The key for geometry change event. @@ -188,13 +190,20 @@ class Measure extends olInteractionInteraction { style: style }); + const source = this.vectorLayer_.getSource(); + if (!(source instanceof VectorSource)) { + throw new Error('Missing source'); + } + const drawInteraction = this.createDrawInteraction(options.sketchStyle, source); + if (!drawInteraction) { + throw new Error('Missing drawInteraction'); + } /** * The draw interaction to be used. * @type {import("ol/interaction/Draw.js").default|import("ngeo/interaction/DrawAzimut.js").default|import("ngeo/interaction/MobileDraw.js").default} * @private */ - this.drawInteraction_ = this.createDrawInteraction(options.sketchStyle, - /** @type {olSourceVector} */(this.vectorLayer_.getSource())); + this.drawInteraction_ = drawInteraction; /** * @type {boolean} @@ -224,13 +233,13 @@ class Measure extends olInteractionInteraction { * @abstract * @param {import("ol/style/Style.js").StyleLike|undefined} * style The sketchStyle used for the drawing interaction. - * @param {import("ol/source/Vector.js").default} source Vector source. - * @return {import("ol/interaction/Draw.js").default|import("ngeo/interaction/DrawAzimut.js").default|import("ngeo/interaction/MobileDraw.js").default} + * @param {VectorSource} source Vector source. + * @return {?import("ol/interaction/Draw.js").default|import("ngeo/interaction/DrawAzimut.js").default|import("ngeo/interaction/MobileDraw.js").default} * The interaction * @protected */ createDrawInteraction(style, source) { - return undefined; + return null; } @@ -262,15 +271,28 @@ class Measure extends olInteractionInteraction { onDrawStart_(evt) { // @ts-ignore: evt should be of type {import('ol/interaction/Draw.js').DrawEvent but he is private this.sketchFeature = evt.feature || evt.detail.feature; - /** @type {olSourceVector} */(this.vectorLayer_.getSource()).clear(true); + const source = this.vectorLayer_.getSource(); + if (!(source instanceof VectorSource)) { + throw new Error('Missing source'); + } + source.clear(true); this.createMeasureTooltip_(); + if (!this.sketchFeature) { + throw new Error('Missing sketchFeature'); + } const geometry = this.sketchFeature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } console.assert(geometry !== undefined); this.changeEventKey_ = olEvents.listen(geometry, 'change', () => { this.handleMeasure((measure, coord) => { if (coord !== null) { + if (!this.measureTooltipElement_) { + throw new Error('Missing measureTooltipElement'); + } this.measureTooltipElement_.innerHTML = measure; this.measureTooltipOverlayCoord_ = coord; } @@ -278,6 +300,12 @@ class Measure extends olInteractionInteraction { }); this.postcomposeEventKey_ = olEvents.listen(this.getMap(), 'postcompose', () => { + if (!this.measureTooltipOverlay_) { + throw new Error('Missing measureTooltipOverlay'); + } + if (!this.measureTooltipOverlayCoord_) { + throw new Error('Missing measureTooltipOverlayCoord'); + } this.measureTooltipOverlay_.setPosition(this.measureTooltipOverlayCoord_); }); } @@ -289,6 +317,15 @@ class Measure extends olInteractionInteraction { * @private */ onDrawEnd_(evt) { + if (!this.measureTooltipElement_) { + throw new Error('Missing measureTooltipElement'); + } + if (!this.measureTooltipOverlay_) { + throw new Error('Missing measureTooltipOverlay'); + } + if (!this.sketchFeature) { + throw new Error('Missing sketchFeature'); + } this.measureTooltipElement_.classList.add('ngeo-tooltip-static'); this.measureTooltipOverlay_.setOffset([0, -7]); /** @type {MeasureEvent} */ @@ -336,8 +373,14 @@ class Measure extends olInteractionInteraction { */ removeHelpTooltip_() { if (this.displayHelpTooltip_) { + if (!this.helpTooltipOverlay_) { + throw new Error('Missing helpTooltipOverlay'); + } this.getMap().removeOverlay(this.helpTooltipOverlay_); if (this.helpTooltipElement_ !== null) { + if (!this.helpTooltipElement_.parentNode) { + throw new Error('Missing helpTooltipElement_.parentNode'); + } this.helpTooltipElement_.parentNode.removeChild(this.helpTooltipElement_); } this.helpTooltipElement_ = null; @@ -371,6 +414,9 @@ class Measure extends olInteractionInteraction { */ removeMeasureTooltip_() { if (this.measureTooltipElement_ !== null) { + if (!this.measureTooltipElement_.parentNode) { + throw new Error('Missing measureTooltipElement_.parentNode'); + } this.measureTooltipElement_.parentNode.removeChild(this.measureTooltipElement_); this.measureTooltipElement_ = null; this.measureTooltipOverlay_ = null; @@ -395,7 +441,14 @@ class Measure extends olInteractionInteraction { this.createHelpTooltip_(); } } else { - /** @type {olSourceVector} */(this.vectorLayer_.getSource()).clear(true); + if (!this.measureTooltipOverlay_) { + throw new Error('Missing measureTooltipOverlay'); + } + const source = this.vectorLayer_.getSource(); + if (!(source instanceof VectorSource)) { + throw new Error('Missing measureTooltipOverlay'); + } + source.clear(true); this.getMap().removeOverlay(this.measureTooltipOverlay_); this.removeMeasureTooltip_(); this.removeHelpTooltip_(); @@ -421,6 +474,9 @@ class Measure extends olInteractionInteraction { * @return {Element} Tooltip Element. */ getTooltipElement() { + if (!this.measureTooltipElement_) { + throw new Error('Missing measureTooltipElement'); + } return this.measureTooltipElement_; } @@ -523,8 +579,17 @@ function handleEvent_(evt) { } const helpMsg = this.sketchFeature === null ? this.startMsg : this.continueMsg; + if (!helpMsg) { + throw new Error('Missing helpMsg'); + } if (this.displayHelpTooltip_) { + if (!this.helpTooltipElement_) { + throw new Error('Missing helpTooltipElement'); + } + if (!this.helpTooltipOverlay_) { + throw new Error('Missing helpTooltipOverlay'); + } olDom.removeChildren(this.helpTooltipElement_); this.helpTooltipElement_.appendChild(helpMsg); this.helpTooltipOverlay_.setPosition(evt.coordinate); diff --git a/src/interaction/MeasureArea.js b/src/interaction/MeasureArea.js index 9c819108430..486092a6a69 100644 --- a/src/interaction/MeasureArea.js +++ b/src/interaction/MeasureArea.js @@ -1,5 +1,6 @@ import ngeoInteractionMeasure, {getFormattedArea} from 'ngeo/interaction/Measure.js'; import olInteractionDraw from 'ol/interaction/Draw.js'; +import Polygon from 'ol/geom/Polygon.js'; /** @@ -16,20 +17,21 @@ export default class extends ngeoInteractionMeasure { constructor(format, gettextCatalog, options = {}) { super(options); - /** - * Message to show after the first point is clicked. - * @type {Element} - */ - this.continueMsg; + let continueMsg; if (options.continueMsg !== undefined) { - this.continueMsg = options.continueMsg; + continueMsg = options.continueMsg; } else { - this.continueMsg = document.createElement('span'); - this.continueMsg.textContent = gettextCatalog.getString('Click to continue drawing the polygon.'); + continueMsg = document.createElement('span'); + continueMsg.textContent = gettextCatalog.getString('Click to continue drawing the polygon.'); const br = document.createElement('br'); br.textContent = gettextCatalog.getString('Double-click or click starting point to finish.'); - this.continueMsg.appendChild(br); + continueMsg.appendChild(br); } + /** + * Message to show after the first point is clicked. + * @type {Element} + */ + this.continueMsg = continueMsg; /** * The format function @@ -39,14 +41,14 @@ export default class extends ngeoInteractionMeasure { } /** - * @param {import("ol/style/Style.js").StyleLike|undefined} style The sketchStyle used for the drawing + * @param {import("ol/style/Style.js").StyleLike} style The sketchStyle used for the drawing * interaction. * @param {import("ol/source/Vector.js").default} source Vector source. * @return {olInteractionDraw|import("ngeo/interaction/MobileDraw.js").default} The interaction */ createDrawInteraction(style, source) { return new olInteractionDraw({ - type: /** @type {import("ol/geom/GeometryType.js").default} */ ('Polygon'), + type: 'Polygon', source: source, style: style }); @@ -56,7 +58,13 @@ export default class extends ngeoInteractionMeasure { * @inheritDoc */ handleMeasure(callback) { - const geom = /** @type {import('ol/geom/Polygon').default} */(this.sketchFeature.getGeometry()); + if (!this.sketchFeature) { + throw new Error('Missing sketchFeature'); + } + const geom = this.sketchFeature.getGeometry(); + if (!(geom instanceof Polygon)) { + throw new Error('Missing geometry'); + } const proj = this.getMap().getView().getProjection(); console.assert(proj); const output = getFormattedArea(geom, proj, this.precision, this.format); diff --git a/src/interaction/MeasureAzimut.js b/src/interaction/MeasureAzimut.js index 496a2d30f5c..947734e02e2 100644 --- a/src/interaction/MeasureAzimut.js +++ b/src/interaction/MeasureAzimut.js @@ -1,6 +1,7 @@ import ngeoInteractionDrawAzimut from 'ngeo/interaction/DrawAzimut.js'; import ngeoInteractionMeasure, {getFormattedLength} from 'ngeo/interaction/Measure.js'; - +import LineString from 'ol/geom/LineString.js'; +import GeometryCollection from 'ol/geom/GeometryCollection.js'; /** * Interaction dedicated to measure length. @@ -16,18 +17,18 @@ export default class extends ngeoInteractionMeasure { */ constructor(unitPrefixFormat, numberFormat, options = {}) { super(options); - + let continueMsg; + if (options.continueMsg !== undefined) { + continueMsg = options.continueMsg; + } else { + continueMsg = document.createElement('span'); + continueMsg.textContent = 'Click to finish.'; + } /** * Message to show after the first point is clicked. * @type {Element} */ - this.continueMsg; - if (options.continueMsg !== undefined) { - this.continueMsg = options.continueMsg; - } else { - this.continueMsg = document.createElement('span'); - this.continueMsg.textContent = 'Click to finish.'; - } + this.continueMsg = continueMsg; /** * The format function @@ -58,10 +59,17 @@ export default class extends ngeoInteractionMeasure { * @inheritDoc */ handleMeasure(callback) { + if (!this.sketchFeature) { + throw new Error('Missing sketchFeature'); + } const geom = this.sketchFeature.getGeometry(); - const line = /** @type {import('ol/geom/LineString.js').default} */( - /** @type {import('ol/geom/GeometryCollection.js').default} */(geom).getGeometries()[0] - ); + if (!(geom instanceof GeometryCollection)) { + throw new Error('Missing geometry'); + } + const line = geom.getGeometries()[0]; + if (!(line instanceof LineString)) { + throw new Error('Missing line'); + } const output = getFormattedAzimutRadius( line, this.getMap().getView().getProjection(), this.decimals, this.precision, this.unitPrefixFormat, this.numberFormat); diff --git a/src/interaction/MeasureLength.js b/src/interaction/MeasureLength.js index 36e3da38a55..ef95bf909dd 100644 --- a/src/interaction/MeasureLength.js +++ b/src/interaction/MeasureLength.js @@ -1,6 +1,6 @@ import ngeoInteractionMeasure, {getFormattedLength} from 'ngeo/interaction/Measure.js'; import olInteractionDraw from 'ol/interaction/Draw.js'; - +import LineString from 'ol/geom/LineString.js'; /** * Interaction dedicated to measure length. @@ -34,14 +34,14 @@ export default class extends ngeoInteractionMeasure { } /** - * @param {import("ol/style/Style.js").StyleLike|undefined} style The sketchStyle used for the drawing + * @param {import("ol/style/Style.js").StyleLike} style The sketchStyle used for the drawing * interaction. * @param {import("ol/source/Vector.js").default} source Vector source. * @return {olInteractionDraw|import("ngeo/interaction/MobileDraw.js").default} The interaction */ createDrawInteraction(style, source) { return new olInteractionDraw({ - type: /** @type {import("ol/geom/GeometryType.js").default} */ ('LineString'), + type: 'LineString', source: source, style: style }); @@ -51,7 +51,13 @@ export default class extends ngeoInteractionMeasure { * @inheritDoc */ handleMeasure(callback) { - const geom = /** @type {import("ol/geom/LineString.js").default} */(this.sketchFeature.getGeometry()); + if (!this.sketchFeature) { + throw new Error('Missing sketchFeature'); + } + const geom = this.sketchFeature.getGeometry(); + if (!(geom instanceof LineString)) { + throw new Error('Missing geometry'); + } const proj = this.getMap().getView().getProjection(); console.assert(proj); const output = getFormattedLength(geom, proj, this.precision, this.format); diff --git a/src/interaction/MeasurePointMobile.js b/src/interaction/MeasurePointMobile.js index 0fb73b2bd31..854a517ad08 100644 --- a/src/interaction/MeasurePointMobile.js +++ b/src/interaction/MeasurePointMobile.js @@ -1,6 +1,6 @@ import ngeoInteractionMeasure, {getFormattedPoint} from 'ngeo/interaction/Measure.js'; import ngeoInteractionMobileDraw from 'ngeo/interaction/MobileDraw.js'; - +import Point from 'ol/geom/Point.js'; /** * Interaction dedicated to measure by coordinate (point) on mobile devices. @@ -35,7 +35,7 @@ export default class extends ngeoInteractionMeasure { */ createDrawInteraction(style, source) { return new ngeoInteractionMobileDraw({ - type: /** @type {import("ol/geom/GeometryType.js").default} */ ('Point'), + type: 'Point', style: style, source: source, }); @@ -45,7 +45,13 @@ export default class extends ngeoInteractionMeasure { * @inheritDoc */ handleMeasure(callback) { - const geom = /** @type {import('ol/geom/Point.js').default} */(this.sketchFeature.getGeometry()); + if (!this.sketchFeature) { + throw new Error('Missing sketchFeature'); + } + const geom = this.sketchFeature.getGeometry(); + if (!(geom instanceof Point)) { + throw new Error('Missing geometry'); + } const dec = this.decimals; const output = getFormattedPoint(geom, dec, this.format_, this.coordFormat_); const coord = geom.getLastCoordinate(); diff --git a/src/interaction/MobileDraw.js b/src/interaction/MobileDraw.js index b317541f1fc..2896197d0b9 100644 --- a/src/interaction/MobileDraw.js +++ b/src/interaction/MobileDraw.js @@ -72,7 +72,7 @@ export default class extends olInteractionInteraction { /** * Sketch feature. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} * @private */ this.sketchFeature_ = null; @@ -86,7 +86,7 @@ export default class extends olInteractionInteraction { /** * Current sketch point. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} * @private */ this.sketchPoint_ = null; @@ -188,6 +188,9 @@ export default class extends olInteractionInteraction { * it. */ addToDrawing() { + if (!this.sketchPoint_) { + throw new Error('Missing sketchPoint'); + } // no need to do anything if interaction is not active, nor drawing const active = this.getActive(); @@ -200,7 +203,7 @@ export default class extends olInteractionInteraction { let sketchFeatureGeom; const sketchPointGeom = this.getSketchPointGeometry_(); const coordinate = sketchPointGeom.getCoordinates(); - let coordinates; + let coordinates = null; // == point == if (this.type_ === 'Point') { @@ -263,6 +266,9 @@ export default class extends olInteractionInteraction { this.set('dirty', false); } + if (!coordinates) { + throw new Error('Missing coordinates'); + } // minPoints validation const valid = this.getValid(); if (this.type_ === 'LineString' || this.type_ === 'Polygon') { @@ -398,6 +404,7 @@ export default class extends olInteractionInteraction { } else { this.startDrawing_(); } + // @ts-ignore this.overlay_.setMap(active ? map : null); } @@ -462,12 +469,14 @@ export default class extends olInteractionInteraction { * @private */ getSketchPointGeometry_() { - console.assert(this.sketchPoint_, 'sketch point should be thruty'); + if (!this.sketchPoint_) { + throw new Error('Missing sketchPoint'); + } const geometry = this.sketchPoint_.getGeometry(); if (geometry instanceof olGeomPoint) { return geometry; } else { - throw 'Wrong geometry type'; + throw new Error('Wrong geometry type'); } } @@ -478,7 +487,9 @@ export default class extends olInteractionInteraction { */ getCenter_() { const center = this.getMap().getView().getCenter(); - console.assert(Array.isArray(center)); + if (!Array.isArray(center)) { + throw new Error('Missing center'); + } return center; } } diff --git a/src/interaction/ModifyCircle.js b/src/interaction/ModifyCircle.js index ba38c0d632b..6c9f7e36e63 100644 --- a/src/interaction/ModifyCircle.js +++ b/src/interaction/ModifyCircle.js @@ -40,7 +40,7 @@ export default class extends olInteractionPointer { /** * Editing vertex. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} * @private */ this.vertexFeature_ = null; @@ -89,7 +89,7 @@ export default class extends olInteractionPointer { * @type {Array} * @private */ - this.dragSegments_ = null; + this.dragSegments_ = []; /** * Draw overlay where sketch features are drawn. @@ -123,7 +123,11 @@ export default class extends olInteractionPointer { * @private */ addFeature_(feature) { - if (feature.getGeometry().getType() === 'Polygon' && + const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + if (geometry.getType() === 'Polygon' && !!feature.get(ngeoFormatFeatureProperties.IS_CIRCLE)) { const geometry = /** @type {import("ol/geom/Polygon.js").default}*/ (feature.getGeometry()); this.writeCircleGeometry_(feature, geometry); diff --git a/src/interaction/ModifyRectangle.js b/src/interaction/ModifyRectangle.js index 2046fed1416..e14153f2142 100644 --- a/src/interaction/ModifyRectangle.js +++ b/src/interaction/ModifyRectangle.js @@ -56,7 +56,7 @@ class ModifyRectangle extends olInteractionPointer { /** * The feature currently modified. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} * @private */ this.feature_ = null; @@ -185,6 +185,9 @@ class ModifyRectangle extends olInteractionPointer { * @private */ initializeParams_() { + if (!this.feature_) { + throw new Error('Missing feature'); + } const feature = this.feature_; // 1. Find the origin (opposite) point for the modify operation @@ -322,6 +325,9 @@ class ModifyRectangle extends olInteractionPointer { */ handleDrag_(evt) { this.willModifyFeatures_(evt); + if (!this.feature_) { + throw new Error('Missing feature'); + } const feature = this.feature_; const geometry = /** @type {import("ol/geom/SimpleGeometry.js").default} */ @@ -329,6 +335,9 @@ class ModifyRectangle extends olInteractionPointer { if (geometry instanceof olGeomPoint) { geometry.setCoordinates(evt.coordinate); + if (!this.params_) { + throw new Error('Missing params'); + } const destinationPixel = evt.pixel; diff --git a/src/interaction/Rotate.js b/src/interaction/Rotate.js index 1f451b994ab..5bddb192efb 100644 --- a/src/interaction/Rotate.js +++ b/src/interaction/Rotate.js @@ -5,7 +5,6 @@ import * as olExtent from 'ol/extent.js'; import olFeature from 'ol/Feature.js'; import * as olEvents from 'ol/events.js'; import olInteractionPointer from 'ol/interaction/Pointer.js'; -import olGeomGeometry from 'ol/geom/Geometry.js'; import olGeomPoint from 'ol/geom/Point.js'; import olGeomLineString from 'ol/geom/LineString.js'; import olGeomPolygon from 'ol/geom/Polygon.js'; @@ -82,19 +81,19 @@ export default class extends olInteractionPointer { /** * The feature currently modified. - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} * @private */ this.feature_ = null; /** - * @type {import("ol/pixel.js").Pixel} + * @type {?import("ol/pixel.js").Pixel} * @private */ this.coordinate_ = null; /** - * @type {import("ol/coordinate.js").Coordinate} + * @type {?import("ol/coordinate.js").Coordinate} * @private */ this.centerCoordinate_ = null; @@ -163,7 +162,9 @@ export default class extends olInteractionPointer { */ addFeature_(feature) { const geometry = feature.getGeometry(); - console.assert(geometry instanceof olGeomGeometry); + if (!geometry) { + throw new Error('Missing geometry'); + } feature.set('angle', 0); @@ -254,7 +255,7 @@ export default class extends olInteractionPointer { } }); if (!found) { - feature = null; + feature = undefined; } } @@ -300,8 +301,19 @@ export default class extends olInteractionPointer { handleDrag_(evt) { this.willModifyFeatures_(evt); - const geometry = /** @type {import("ol/geom/SimpleGeometry.js").default} */ - (this.feature_.getGeometry()); + if (!this.feature_) { + throw new Error('Missing feature'); + } + if (!this.coordinate_) { + throw new Error('Missing coordinate'); + } + if (!this.centerCoordinate_) { + throw new Error('Missing centerCoordinate'); + } + const geometry = this.feature_.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } const oldX = this.coordinate_[0]; const oldY = this.coordinate_[1]; @@ -331,6 +343,9 @@ export default class extends olInteractionPointer { */ handleUp_(evt) { if (this.modified_) { + if (!this.feature_) { + throw new Error('Missing feature'); + } /** @type {RotateEvent} */ const event = new ngeoCustomEvent('rotateend', {feature: this.feature_}); this.dispatchEvent(event); diff --git a/src/interaction/Translate.js b/src/interaction/Translate.js index 86d224dcf18..14a9edc8b93 100644 --- a/src/interaction/Translate.js +++ b/src/interaction/Translate.js @@ -57,7 +57,7 @@ export default class extends olInteractionTranslate { this.keyPressListenerKey_ = null; /** - * @type {import("ol/Collection.js").default.} + * @type {?import("ol/Collection.js").default.} * @private */ this.myFeatures_ = options.features !== undefined ? options.features : null; @@ -125,6 +125,7 @@ export default class extends olInteractionTranslate { const currentMap = this.getMap(); if (currentMap) { + // @ts-ignore this.vectorLayer_.setMap(null); } @@ -147,6 +148,9 @@ export default class extends olInteractionTranslate { } const active = this.getActive(); const features = this.myFeatures_; + if (!features) { + throw new Error('Missing features'); + } const keys = this.listenerKeys_; if (map && active && features) { @@ -198,7 +202,9 @@ export default class extends olInteractionTranslate { addFeature_(feature) { const uid = olUtilGetUid(feature); const geometry = feature.getGeometry(); - console.assert(geometry instanceof olGeomGeometry); + if (!geometry) { + throw new Error('Missing geometry'); + } this.featureListenerKeys_[uid] = olEvents.listen( geometry, @@ -267,8 +273,9 @@ export default class extends olInteractionTranslate { point = new olGeomPoint(center); } - console.assert(point, 'Point should be thruthy'); - + if (!point) { + throw new Error('Missing point'); + } return point; } diff --git a/src/interaction/common.js b/src/interaction/common.js index 2df50385404..c0078579697 100644 --- a/src/interaction/common.js +++ b/src/interaction/common.js @@ -19,7 +19,11 @@ import {createEditingStyle} from 'ol/style/Style.js'; export function getDefaultDrawStyleFunction() { const style = createEditingStyle(); return function(feature, resolution) { - return style[feature.getGeometry().getType()]; + const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } + return style[geometry.getType()]; }; } @@ -31,6 +35,6 @@ export function getDefaultDrawStyleFunction() { export function getDefaultModifyStyleFunction() { const style = createEditingStyle(); return function(feature, resolution) { - return style[/**@type {import("ol/geom/GeometryType.js").default} */ ('Point')]; + return style.Point; }; } diff --git a/src/layertree/Controller.js b/src/layertree/Controller.js index cd8eb385ef0..0c1cb128930 100644 --- a/src/layertree/Controller.js +++ b/src/layertree/Controller.js @@ -282,7 +282,7 @@ LayertreeController.prototype.getCalculateState = function() { return this.state_; } /** @type {string} */ - let childState; + let childState = ''; /** @type {string} */ let previousChildState; this.children.some((child) => { @@ -294,6 +294,7 @@ LayertreeController.prototype.getCalculateState = function() { } } previousChildState = childState; + return false; }); return childState; }; @@ -395,7 +396,7 @@ LayertreeController.prototype.traverseDepthFirst = function(visitor) { } return false; // continue traversing default: - console.assert(false, 'Unhandled case'); + throw 'Unhandled case'; } }; diff --git a/src/map/BackgroundLayerMgr.js b/src/map/BackgroundLayerMgr.js index 51c102d67fe..f8013a1c65f 100644 --- a/src/map/BackgroundLayerMgr.js +++ b/src/map/BackgroundLayerMgr.js @@ -13,7 +13,7 @@ import ngeoLayerHelper from 'ngeo/map/LayerHelper.js'; /** * @typedef {Object} BackgroundEventDetails * @property {import("ol/layer/Base.js").default} current - * @property {import("ol/layer/Base.js").default} previous + * @property {?import("ol/layer/Base.js").default} previous */ @@ -94,7 +94,7 @@ export class MapBackgroundLayerManager extends olObservable { * Return the current background layer of a given map. `null` is returned if * the map does not have a background layer. * @param {import("ol/Map.js").default} map Map. - * @return {import("ol/layer/Base.js").default} layer The background layer. + * @return {?import("ol/layer/Base.js").default} layer The background layer. */ get(map) { const mapUid = olUtilGetUid(map).toString(); @@ -107,7 +107,7 @@ export class MapBackgroundLayerManager extends olObservable { * is removed. * @param {import("ol/Map.js").default} map The map. * @param {import("ol/layer/Base.js").default} layer The new background layer. - * @return {import("ol/layer/Base.js").default} The previous background layer. + * @return {?import("ol/layer/Base.js").default} The previous background layer. */ set(map, layer) { const ZIndex = -200; @@ -146,7 +146,7 @@ export class MapBackgroundLayerManager extends olObservable { * Return the current background layer overlay of a given map, used by the opacity slider. * `null` is returned if the map does not have an opacity background layer. * @param {import("ol/Map.js").default} map Map. - * @return {import("ol/layer/Base.js").default} layer The opacity background layer. + * @return {?import("ol/layer/Base.js").default} layer The opacity background layer. */ getOpacityBgLayer(map) { const mapUid = olUtilGetUid(map).toString(); @@ -161,10 +161,13 @@ export class MapBackgroundLayerManager extends olObservable { */ setOpacityBgLayer(map, layer) { const bgGroup = this.ngeoLayerHelper_.getGroupFromMap(map, BACKGROUNDLAYERGROUP_NAME); - const previous = bgGroup.getLayers().remove(this.getOpacityBgLayer(map)); + const opacityBackgroundLayer = this.getOpacityBgLayer(map); + if (opacityBackgroundLayer) { + const previous = bgGroup.getLayers().remove(opacityBackgroundLayer); + layer.setOpacity(previous ? previous.getOpacity() : 0); + layer.setVisible(previous ? previous.getVisible() : true); + } const ZIndex = -100; - layer.setOpacity(previous ? previous.getOpacity() : 0); - layer.setVisible(previous ? previous.getVisible() : true); layer.setZIndex(ZIndex); this.ngeoLayerHelper_.setZIndexToFirstLevelChildren(layer, ZIndex); diff --git a/src/map/FeatureOverlay.js b/src/map/FeatureOverlay.js index b4b95f3d757..a3524787fe3 100644 --- a/src/map/FeatureOverlay.js +++ b/src/map/FeatureOverlay.js @@ -19,7 +19,7 @@ export function FeatureOverlay(manager, index) { this.manager_ = manager; /** - * @type {import("ol/Collection.js").default.} + * @type {?import("ol/Collection.js").default.} * @private */ this.features_ = null; diff --git a/src/map/LayerHelper.js b/src/map/LayerHelper.js index b696b619347..9cda86895aa 100644 --- a/src/map/LayerHelper.js +++ b/src/map/LayerHelper.js @@ -72,7 +72,7 @@ const REFRESH_PARAM = 'random'; * @param {string=} opt_serverType Type of the server ("mapserver", * "geoserver", "qgisserver", …). * @param {string=} opt_time time parameter for layer queryable by time/periode - * @param {Object.=} opt_params WMS parameters. + * @param {Object=} opt_params WMS parameters. * @param {string=} opt_crossOrigin crossOrigin. * @param {Object=} opt_customSourceOptions Some initial options. * @param {Object=} opt_customLayerOptions The layer opacity. @@ -88,10 +88,10 @@ LayerHelper.prototype.createBasicWMSLayer = function(sourceURL, }; let olServerType; if (opt_time) { - params['TIME'] = opt_time; + params.TIME = opt_time; } if (opt_serverType) { - params['SERVERTYPE'] = opt_serverType; + params.SERVERTYPE = opt_serverType; // OpenLayers expects 'qgis' insteads of 'qgisserver' olServerType = opt_serverType.replace(ServerType.QGISSERVER, 'qgis'); } @@ -122,7 +122,9 @@ LayerHelper.prototype.createBasicWMSLayerFromDataSource = function( dataSource, opt_crossOrigin ) { const url = dataSource.wmsUrl; - console.assert(url); + if (url === undefined) { + throw new Error('Missing url'); + } const layerNames = dataSource.getOGCLayerNames().join(','); const serverType = dataSource.ogcServerType; @@ -159,9 +161,9 @@ LayerHelper.prototype.createBasicWMSLayerFromDataSource = function( * @param {string} capabilitiesURL The getCapabilities url. * @param {string} layerName The name of the layer. * @param {string=} opt_matrixSet Optional WMTS matrix set. - * @param {Object.=} opt_dimensions WMTS dimensions. + * @param {Object=} opt_dimensions WMTS dimensions. * @param {Object=} opt_customOptions Some initial options. - * @return {angular.IPromise.} A Promise with a layer (with source) on + * @return {angular.IPromise} A Promise with a layer (with source) on * success, no layer else. */ LayerHelper.prototype.createWMTSLayerFromCapabilitites = function( @@ -191,9 +193,12 @@ LayerHelper.prototype.createWMTSLayerFromCapabilitites = function( layer.setSource(source); // Add styles from capabilities as param of the layer - const layers = result['Contents']['Layer']; - const l = olArray.find(layers, (elt, index, array) => elt['Identifier'] == layerName); - layer.set('capabilitiesStyles', l['Style']); + const layers = result.Contents.Layer; + const l = olArray.find(layers, (elt, index, array) => elt.Identifier == layerName); + if (!l) { + return $q.reject(`Layer ${layerName} not abalable in WMTS capabilities from ${capabilitiesURL}`); + } + layer.set('capabilitiesStyles', l.Style); return $q.resolve(layer); } @@ -217,7 +222,7 @@ LayerHelper.prototype.createWMTSLayerFromCapabilititesObj = function( const options = optionsFromCapabilities(capabilities, { crossOrigin: 'anonymous', - layer: layerCap['Identifier'] + layer: layerCap.Identifier }); console.assert(options); @@ -231,7 +236,7 @@ LayerHelper.prototype.createWMTSLayerFromCapabilititesObj = function( preload: Infinity, source: source }); - result.set('capabilitiesStyles', layerCap['Style']); + result.set('capabilitiesStyles', layerCap.Style); return result; }; @@ -368,9 +373,9 @@ LayerHelper.prototype.getWMTSLegendURL = function(layer) { let url; const styles = layer.get('capabilitiesStyles'); if (styles !== undefined) { - const legendURL = styles[0]['legendURL']; + const legendURL = styles[0].legendURL; if (legendURL !== undefined) { - url = legendURL[0]['href']; + url = legendURL[0].href; } } return url; @@ -407,20 +412,20 @@ LayerHelper.prototype.getWMSLegendURL = function(url, 'LAYER': layerName }; if (opt_scale !== undefined) { - queryString['SCALE'] = opt_scale; + queryString.SCALE = opt_scale; } if (opt_legendRule !== undefined) { - queryString['RULE'] = opt_legendRule; + queryString.RULE = opt_legendRule; if (opt_legendWidth !== undefined) { - queryString['WIDTH'] = opt_legendWidth; + queryString.WIDTH = opt_legendWidth; } if (opt_legendHeight !== undefined) { - queryString['HEIGHT'] = opt_legendHeight; + queryString.HEIGHT = opt_legendHeight; } } if (opt_servertype == 'qgis') { if (opt_dpi != undefined) { - queryString['DPI'] = opt_dpi; + queryString.DPI = opt_dpi; } if ( opt_bbox != undefined @@ -429,10 +434,10 @@ LayerHelper.prototype.getWMSLegendURL = function(url, && opt_dpi != undefined && opt_legendRule == undefined ) { - queryString['BBOX'] = opt_bbox.join(','); - queryString['SRS'] = opt_srs; - queryString['WIDTH'] = Math.round((opt_bbox[2] - opt_bbox[0]) / opt_scale * 39.37 * opt_dpi); - queryString['HEIGHT'] = Math.round((opt_bbox[3] - opt_bbox[1]) / opt_scale * 39.37 * opt_dpi); + queryString.BBOX = opt_bbox.join(','); + queryString.SRS = opt_srs; + queryString.WIDTH = Math.round((opt_bbox[2] - opt_bbox[0]) / opt_scale * 39.37 * opt_dpi); + queryString.HEIGHT = Math.round((opt_bbox[3] - opt_bbox[1]) / opt_scale * 39.37 * opt_dpi); } } if (opt_additionalQueryString) { @@ -454,6 +459,9 @@ LayerHelper.prototype.isLayerVisible = function(layer, map) { } const currentResolution = map.getView().getResolution(); + if (currentResolution === undefined) { + throw new Error('Missing resolution'); + } return currentResolution > layer.getMinResolution() && currentResolution < layer.getMaxResolution(); }; diff --git a/src/map/scaleselector.js b/src/map/scaleselector.js index 468a065734d..a1f39443788 100644 --- a/src/map/scaleselector.js +++ b/src/map/scaleselector.js @@ -127,9 +127,9 @@ class ScaleselectorController { console.assert(this.scales !== undefined); /** - * @type {Array.} + * @type {Array} */ - this.zoomLevels; + this.zoomLevels = []; $scope.$watch(() => Object.keys(this.scales).length, (newLength) => { this.zoomLevels = Object.keys(this.scales).map(Number); @@ -202,7 +202,7 @@ class ScaleselectorController { } /** - * @param {Event|import("ol/events/Event.js").default} e OpenLayers object event. + * @param {?Event|import("ol/events/Event.js").default} e OpenLayers object event. * @private */ handleResolutionChange_(e) { diff --git a/src/measure/area.js b/src/measure/area.js index 1fa13d26e0a..bca92289962 100644 --- a/src/measure/area.js +++ b/src/measure/area.js @@ -33,20 +33,26 @@ function measureAreaComponent($compile, gettextCatalog, $filter, $injector) { * @param {!angular.IScope} $scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} drawFeatureCtrl Controller. + * @param {angular.IController=} drawFeatureCtrl Controller. */ link: ($scope, element, attrs, drawFeatureCtrl) => { + if (!drawFeatureCtrl) { + throw new Error('Missing drawFeatureCtrl'); + } const helpMsg = gettextCatalog.getString('Click to start drawing polygon'); const contMsg = gettextCatalog.getString('Click to continue drawing
' + 'Double-click or click starting point to finish'); - const measureArea = new ngeoInteractionMeasureArea($filter('ngeoUnitPrefix'), gettextCatalog, { + const options = { style: new olStyleStyle(), startMsg: $compile(`
${helpMsg}
`)($scope)[0], continueMsg: $compile(`
${contMsg}
`)($scope)[0], - precision: $injector.has('ngeoMeasurePrecision') ? $injector.get('ngeoMeasurePrecision') : undefined - }); + }; + if ($injector.has('ngeoMeasurePrecision')) { + options.precision = $injector.get('ngeoMeasurePrecision'); + } + const measureArea = new ngeoInteractionMeasureArea($filter('ngeoUnitPrefix'), gettextCatalog, options); drawFeatureCtrl.registerInteraction(measureArea); drawFeatureCtrl.measureArea = measureArea; diff --git a/src/measure/azimut.js b/src/measure/azimut.js index 546555c3a7a..f1046bbf878 100644 --- a/src/measure/azimut.js +++ b/src/measure/azimut.js @@ -37,22 +37,29 @@ function measureAzimutComponent($compile, gettextCatalog, $filter, $injector) { * @param {!angular.IScope} $scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} drawFeatureCtrl Controller. + * @param {angular.IController=} drawFeatureCtrl Controller. */ link: ($scope, element, attrs, drawFeatureCtrl) => { + if (!drawFeatureCtrl) { + throw new Error('Missing drawFeatureCtrl'); + } const helpMsg = gettextCatalog.getString('Click to start drawing circle'); const contMsg = gettextCatalog.getString('Click to finish'); + const options = { + style: new olStyleStyle(), + startMsg: $compile(`
${helpMsg}
`)($scope)[0], + continueMsg: $compile(`
${contMsg}
`)($scope)[0], + }; + if ($injector.get('ngeoMeasurePrecision')) { + options.precision = $injector.get('ngeoMeasurePrecision'); + } + if ($injector.get('ngeoMeasureDecimals')) { + options.decimals = $injector.get('ngeoMeasureDecimals'); + } const measureAzimut = new ngeoInteractionMeasureAzimut( - $filter('ngeoUnitPrefix'), $filter('number'), { - style: new olStyleStyle(), - startMsg: $compile(`
${helpMsg}
`)($scope)[0], - continueMsg: $compile(`
${contMsg}
`)($scope)[0], - precision: $injector.has('ngeoMeasurePrecision') ? - $injector.get('ngeoMeasurePrecision') : undefined, - decimals: $injector.has('ngeoMeasureDecimals') ? $injector.get('ngeoMeasureDecimals') : undefined - }); + $filter('ngeoUnitPrefix'), $filter('number'), options); drawFeatureCtrl.registerInteraction(measureAzimut); drawFeatureCtrl.measureAzimut = measureAzimut; diff --git a/src/measure/length.js b/src/measure/length.js index 0be9f0fb1b1..5c3897aacbb 100644 --- a/src/measure/length.js +++ b/src/measure/length.js @@ -35,20 +35,28 @@ function measureLengthComponent($compile, gettextCatalog, $filter, $injector) { * @param {!angular.IScope} $scope Scope. * @param {JQuery} element Element. * @param {angular.IAttributes} attrs Attributes. - * @param {angular.IController} drawFeatureCtrl Controller. + * @param {angular.IController=} drawFeatureCtrl Controller. */ link: ($scope, element, attrs, drawFeatureCtrl) => { + if (!drawFeatureCtrl) { + throw new Error('Missing drawFeatureCtrl'); + } const helpMsg = gettextCatalog.getString('Click to start drawing line'); const contMsg = gettextCatalog.getString('Click to continue drawing
' + 'Double-click or click last point to finish'); - const measureLength = new ngeoInteractionMeasureLength($filter('ngeoUnitPrefix'), gettextCatalog, { + const options = { style: new olStyleStyle(), startMsg: $compile(`
${helpMsg}
`)($scope)[0], continueMsg: $compile(`
${contMsg}
`)($scope)[0], - precision: $injector.has('ngeoMeasurePrecision') ? $injector.get('ngeoMeasurePrecision') : undefined - }); + }; + if ($injector.has('ngeoMeasurePrecision')) { + options.precision = $injector.get('ngeoMeasurePrecision'); + } + const measureLength = new ngeoInteractionMeasureLength( + $filter('ngeoUnitPrefix'), gettextCatalog, options + ); drawFeatureCtrl.registerInteraction(measureLength); drawFeatureCtrl.measureLength = measureLength; diff --git a/src/message/displaywindowComponent.js b/src/message/displaywindowComponent.js index 395c8e57e73..f7ae014d924 100644 --- a/src/message/displaywindowComponent.js +++ b/src/message/displaywindowComponent.js @@ -68,7 +68,7 @@ class Controller { /** * @type {boolean} */ - this.clearOnClose; + this.clearOnClose = false; /** * @type {?string} @@ -88,32 +88,32 @@ class Controller { /** * @type {boolean} */ - this.draggable; + this.draggable = false; /** * @type {Element|string} */ - this.draggableContainment; + this.draggableContainment = ''; /** * @type {boolean} */ - this.desktop; + this.desktop = false; /** - * @type {?string} + * @type {string} */ - this.height = null; + this.height = ''; /** * @type {boolean} */ - this.open; + this.open = false; /** * @type {boolean} */ - this.resizable; + this.resizable = false; /** * @type {?string} @@ -126,9 +126,9 @@ class Controller { this.url = null; /** - * @type {?string} + * @type {string} */ - this.width = null; + this.width = ''; // === Injected Properties === @@ -209,6 +209,9 @@ class Controller { * @private */ updateContentTemplate_() { + if (!this.contentTemplate) { + return; + } const scope = this.contentScope || this.scope_; const compiled = this.compile_(this.contentTemplate)(scope); const displayWindow = this.element_.find( @@ -228,12 +231,12 @@ class Controller { } /** - * @return {!Object.} CSS style when using width/height + * @return {Object} CSS style when using width/height */ get style() { return { - 'height': this.height, - 'width': this.width + height: this.height, + width: this.width }; } diff --git a/src/message/modalComponent.js b/src/message/modalComponent.js index a20faaf3a7d..19df4056ac4 100644 --- a/src/message/modalComponent.js +++ b/src/message/modalComponent.js @@ -94,29 +94,29 @@ class Controller { /** * @private - * @type {JQuery} + * @type {?JQuery} */ - this.modal_; + this.modal_ = null; /** * @type {string} */ - this.draggableHandle; + this.draggableHandle = ''; /** * @type {boolean} */ - this.closable; + this.closable = false; /** * @type {boolean} */ - this.resizable; + this.resizable = false; /** - * @type {angular.INgModelController|null} + * @type {?angular.INgModelController} */ - this.ngModel; + this.ngModel = null; } $onInit() { @@ -144,11 +144,23 @@ class Controller { dialog.resizable(); } + if (!this.ngModel) { + throw new Error('Missing model'); + } this.ngModel.$render = () => { + if (!this.ngModel) { + throw new Error('Missing model'); + } + if (!this.modal_) { + throw new Error('Missing modal'); + } this.modal_.modal(this.ngModel.$viewValue ? 'show' : 'hide'); }; this.modal_.on('shown.bs.modal hidden.bs.modal', (e) => { + if (!this.ngModel) { + throw new Error('Missing model'); + } const type = e.type; console.assert(type == 'shown' || type == 'hidden'); this.ngModel.$setViewValue(type == 'shown'); @@ -156,6 +168,9 @@ class Controller { } $onDestroy() { + if (!this.modal_) { + throw new Error('Missing modal'); + } // Force close the modal. this.modal_.modal('hide'); // Destroy the children's plugins. diff --git a/src/message/popoverComponent.js b/src/message/popoverComponent.js index 82c1e3f4d56..67b0e1bd19e 100644 --- a/src/message/popoverComponent.js +++ b/src/message/popoverComponent.js @@ -35,6 +35,9 @@ function messagePopoverComponent() { scope: true, controller: 'NgeoPopoverController as popoverCtrl', link: (scope, elem, attrs, ngeoPopoverCtrl) => { + if (!ngeoPopoverCtrl) { + throw new Error('Missing ngeoPopoverCtrl'); + } ngeoPopoverCtrl.anchorElm.on('inserted.bs.popover', () => { ngeoPopoverCtrl.bodyElm.show(); @@ -75,6 +78,9 @@ function messagePopoverAnchorComponent() { restrict: 'A', require: '^^ngeoPopover', link: (scope, elem, attrs, ngeoPopoverCtrl) => { + if (!ngeoPopoverCtrl) { + throw new Error('Missing ngeoPopoverCtrl'); + } ngeoPopoverCtrl.anchorElm = elem; } }; @@ -91,6 +97,9 @@ function messagePopoverContentComponent() { restrict: 'A', require: '^^ngeoPopover', link: (scope, elem, attrs, ngeoPopoverCtrl) => { + if (!ngeoPopoverCtrl) { + throw new Error('Missing ngeoPopoverCtrl'); + } ngeoPopoverCtrl.bodyElm = elem; elem.hide(); } @@ -115,16 +124,22 @@ function PopoverController($scope) { this.shown = false; /** - * @type {JQuery|undefined} + * @type {?JQuery} */ - this.anchorElm = undefined; + this.anchorElm = null; /** - * @type {JQuery|undefined} + * @type {?JQuery} */ - this.bodyElm = undefined; + this.bodyElm = null; const clickHandler = (clickEvent) => { + if (!this.anchorElm) { + throw new Error('Missing anchorElm'); + } + if (!this.bodyElm) { + throw new Error('Missing bodyElm'); + } if (this.anchorElm[0] !== clickEvent.target && this.bodyElm.parent()[0] !== clickEvent.target && this.bodyElm.parent().find(clickEvent.target).length === 0 && this.shown) { @@ -144,6 +159,9 @@ function PopoverController($scope) { * Dissmiss popover function */ PopoverController.prototype.dismissPopover = function() { + if (!this.anchorElm) { + throw new Error('Missing anchorElm'); + } this.shown = false; this.anchorElm.popover('hide'); }; diff --git a/src/misc/AutoProjection.js b/src/misc/AutoProjection.js index d7452b71019..c9007b1715e 100644 --- a/src/misc/AutoProjection.js +++ b/src/misc/AutoProjection.js @@ -71,7 +71,7 @@ AutoProjectionService.prototype.getProjectionList = function(projectionsCodes) { */ AutoProjectionService.prototype.tryProjections = function(coordinates, extent, viewProjection, opt_projections) { - let position; + let position = null; if (opt_projections === undefined) { opt_projections = [viewProjection]; } @@ -85,6 +85,7 @@ AutoProjectionService.prototype.tryProjections = function(coordinates, // Wrong coordinate leads to a transform error and ol throw an exception that we won't log. } position = null; + return false; }); return position; }; diff --git a/src/misc/FeatureHelper.js b/src/misc/FeatureHelper.js index 532b9f29e2c..4853bfb635d 100644 --- a/src/misc/FeatureHelper.js +++ b/src/misc/FeatureHelper.js @@ -112,7 +112,7 @@ export function FeatureHelper($injector, $filter) { /** * Filter function to display point coordinates or null to don't use any filter. - * @type {angular.IFilterFilter} + * @type {?angular.IFilterFilter} * @private */ this.pointFilterFn_ = null; @@ -136,10 +136,10 @@ export function FeatureHelper($injector, $filter) { } /** - * @type {!import("ol/proj/Projection.js").default} + * @type {?import("ol/proj/Projection.js").default} * @private */ - this.projection_; + this.projection_ = null; /** * Download service. @@ -210,14 +210,16 @@ FeatureHelper.prototype.getStyle = function(feature) { break; } - console.assert(style, 'Style should be thruthy'); + if (!style) { + throw new Error('Missing style'); + } /** @type {!Array} */ let styles; - if (style.constructor === Array) { - styles = /** @type {!Array.}*/ (style); + if (style instanceof Array) { + styles = style; } else { - styles = [/** @type {!import("ol/style/Style.js").default}*/(style)]; + styles = [style]; } return styles; @@ -377,6 +379,9 @@ FeatureHelper.prototype.getPolygonStyle_ = function(feature) { })]; if (showMeasure || showLabel) { if (showMeasure && azimut !== undefined) { + if (!this.projection_) { + throw new Error('Missing projection'); + } // Radius style: const line = this.getRadiusLine(feature, azimut); const length = getFormattedLength( @@ -484,7 +489,9 @@ FeatureHelper.prototype.createEditingStyles = function(feature) { const styles = []; const geom = feature.getGeometry(); - console.assert(geom); + if (!geom) { + throw new Error('Missing geom'); + } const type = geom.getType(); if (type === 'Point') { @@ -575,7 +582,7 @@ FeatureHelper.prototype.getVertexInfoAtCoordinate = function( let coordinates = null; let coordinatess = null; let coordinatesss = null; - let minNumCoordinates; + let minNumCoordinates = -1; const geometry = feature.getGeometry(); if (geometry instanceof olGeomLineString) { @@ -756,19 +763,26 @@ FeatureHelper.prototype.getVertexStyle = function(opt_incGeomFunc) { FeatureHelper.prototype.removeVertex = function(feature, vertexInfo) { let deleted = false; - const geometry = /** @type {olGeomSimpleGeometry} */(feature.getGeometry()); - console.assert(geometry instanceof olGeomSimpleGeometry); - const coordinates = geometry.getCoordinates(); + const geometry = feature.getGeometry(); + if (!(geometry instanceof olGeomSimpleGeometry)) { + throw new Error('Wrong geometry type'); + } if (geometry instanceof olGeomLineString) { // LineString + const coordinates = geometry.getCoordinates(); const index = vertexInfo[0]; if (coordinates.length > 2) { coordinates.splice(index, 1); deleted = true; } + + if (deleted) { + geometry.setCoordinates(coordinates); + } } else if (geometry instanceof olGeomPolygon) { // Polygon + const coordinates = geometry.getCoordinates(); const indexOne = vertexInfo[0]; const indexTwo = vertexInfo[1]; const component = coordinates[indexOne]; @@ -781,16 +795,27 @@ FeatureHelper.prototype.removeVertex = function(feature, vertexInfo) { component.push(component[0]); } } + + if (deleted) { + geometry.setCoordinates(coordinates); + } } else if (geometry instanceof olGeomMultiLineString) { // MultiLineString + const coordinates = geometry.getCoordinates(); const indexOne = vertexInfo[0]; const indexTwo = vertexInfo[1]; - if (coordinates[indexOne].length > 2) { - coordinates[indexOne].splice(indexTwo, 1); + const component = coordinates[indexOne]; + if (component.length > 2) { + component.splice(indexTwo, 1); deleted = true; } + + if (deleted) { + geometry.setCoordinates(coordinates); + } } else if (geometry instanceof olGeomMultiPolygon) { // MultiPolygon + const coordinates = geometry.getCoordinates(); const indexOne = vertexInfo[0]; const indexTwo = vertexInfo[1]; const indexThree = vertexInfo[2]; @@ -804,10 +829,10 @@ FeatureHelper.prototype.removeVertex = function(feature, vertexInfo) { component.push(component[0]); } } - } - if (deleted) { - geometry.setCoordinates(coordinates); + if (deleted) { + geometry.setCoordinates(coordinates); + } } }; @@ -898,8 +923,9 @@ FeatureHelper.prototype.getHaloStyle_ = function(feature) { break; } - console.assert(style, 'Style should be thruthy'); - + if (!style) { + throw new Error('Missing style'); + } return style; }; @@ -915,9 +941,9 @@ FeatureHelper.prototype.getHaloStyle_ = function(feature) { */ export function getFilteredFeatureValues(feature) { const properties = feature.getProperties(); - delete properties['boundedBy']; + delete properties.boundedBy; delete properties[feature.getGeometryName()]; - delete properties['ngeo_feature_type_']; + delete properties.ngeo_feature_type_; return properties; } @@ -926,8 +952,7 @@ export function getFilteredFeatureValues(feature) { * @return {number} Angle. */ FeatureHelper.prototype.getAngleProperty = function(feature) { - const angle = +(/** @type {string} */ ( - feature.get(ngeoFormatFeatureProperties.ANGLE))); + const angle = +(/** @type {string} */ (feature.get(ngeoFormatFeatureProperties.ANGLE))); console.assert(typeof angle == 'number'); return angle; }; @@ -938,11 +963,8 @@ FeatureHelper.prototype.getAngleProperty = function(feature) { * @return {string} Color. */ FeatureHelper.prototype.getColorProperty = function(feature) { - const color = feature.get(ngeoFormatFeatureProperties.COLOR); - console.assert(typeof color == 'string'); - return color; }; @@ -1054,7 +1076,8 @@ FeatureHelper.prototype.exportGPX = function(features) { const format = new olFormatGPX(); const mimeType = 'application/gpx+xml'; const fileName = 'export.gpx'; - this.export_(features, format, fileName, mimeType); + // Typecast due OL issue ... + this.export_(features, /** @type {import("ol/format/Feature.js").default} */(format), fileName, mimeType); }; @@ -1067,7 +1090,8 @@ FeatureHelper.prototype.exportKML = function(features) { const format = new olFormatKML(); const mimeType = 'application/vnd.google-earth.kml+xml'; const fileName = 'export.kml'; - this.export_(features, format, fileName, mimeType); + // Typecast due OL issue ... + this.export_(features, /** @type {import("ol/format/Feature.js").default} */(format), fileName, mimeType); }; @@ -1075,8 +1099,8 @@ FeatureHelper.prototype.exportKML = function(features) { * Export features using a given format to a specific filename and download * the result to the browser. The projection of the exported features is: * `EPSG:4326`. - * @param {!Array.} features Array of vector features. - * @param {!import("ol/format/Feature.js").default} format Format + * @param {Array} features Array of vector features. + * @param {import("ol/format/Feature.js").default} format Format * @param {string} fileName Name of the file. * @param {string=} opt_mimeType Mime type. Defaults to 'text/plain'. * @private @@ -1148,16 +1172,23 @@ FeatureHelper.prototype.createTextStyle_ = function(options) { * @return {string} Measure. */ FeatureHelper.prototype.getMeasure = function(feature) { + if (!this.projection_) { + throw new Error('Missing projection'); + } const geometry = feature.getGeometry(); - console.assert(geometry, 'Geometry should be truthy'); + if (!geometry) { + throw new Error('Missing geometry'); + } let measure = ''; if (geometry instanceof olGeomPolygon) { if (this.getType(feature) === ngeoGeometryType.CIRCLE) { const azimut = this.optNumber(feature, ngeoFormatFeatureProperties.AZIMUT); - console.assert(typeof azimut == 'number'); + if (typeof azimut != 'number') { + throw new Error('Missing azimut'); + } const line = this.getRadiusLine(feature, azimut); measure = getFormattedAzimutRadius( @@ -1223,8 +1254,9 @@ FeatureHelper.prototype.getType = function(feature) { type = ngeoGeometryType.MULTI_LINE_STRING; } - console.assert(type, 'Type should be thruthy'); - + if (!type) { + throw new Error('Missing type'); + } return type; }; @@ -1249,15 +1281,22 @@ FeatureHelper.prototype.fitMapToFeature = function(feature, map, opt_duration) { const duration = opt_duration !== undefined ? opt_duration : 250; const size = map.getSize(); - console.assert(Array.isArray(size)); + if (!Array.isArray(size)) { + throw new Error('Missing size'); + } const view = map.getView(); const viewExtent = view.calculateExtent(size); const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } const geomIsVisible = geometry.intersectsExtent(viewExtent); const mapCenter = view.getCenter(); - console.assert(Array.isArray(mapCenter)); + if (!Array.isArray(mapCenter)) { + throw new Error('Missing mapCenter'); + } const featureExtent = geometry.getExtent(); @@ -1267,9 +1306,15 @@ FeatureHelper.prototype.fitMapToFeature = function(feature, map, opt_duration) { // == Action: Zoom out == // if the geometry is visible const featureResolution = view.getResolutionForExtent(featureExtent); - const featureZoom = Math.floor( - view.getZoomForResolution(featureResolution)); + const featureZoomTmp = view.getZoomForResolution(featureResolution); + if (!featureZoomTmp) { + throw new Error('Missing featureZoom'); + } + const featureZoom = Math.floor(featureZoomTmp); const zoom = view.getZoom(); + if (!zoom) { + throw new Error('Missing zoom'); + } if (featureZoom < zoom) { view.animate({ center: mapCenter, @@ -1333,6 +1378,9 @@ FeatureHelper.prototype.fitMapToFeature = function(feature, map, opt_duration) { */ FeatureHelper.prototype.getRadiusLine = function(feature, azimut) { const geometry = feature.getGeometry(); + if (!geometry) { + throw new Error('Missing geometry'); + } // Determine the radius for the circle const extent = geometry.getExtent(); const radius = (extent[3] - extent[1]) / 2; diff --git a/src/misc/File.js b/src/misc/File.js index dbb41887fd7..2264ecf712c 100644 --- a/src/misc/File.js +++ b/src/misc/File.js @@ -44,12 +44,21 @@ export function FileService($q, $http, gettext) { } fileReader = new FileReader(); fileReader.onload = function(evt) { - const target = /** @type {FileReader} */(evt.target); + const target = evt.target; + if (!(target instanceof FileReader)) { + throw new Error('Wrong target type'); + } defer.resolve(target.result); }; fileReader.onerror = function(evt) { - const target = /** @type {FileReader} */(evt.target); + const target = evt.target; + if (!(target instanceof FileReader)) { + throw new Error('Wrong target type'); + } const err = target.error; + if (!err) { + throw new Error('Missing error'); + } console.error('Reading file failed: ', err); defer.reject({ 'message': err.code == 20 ? gettext('Operation canceled') : gettext('Read failed'), diff --git a/src/misc/Time.js b/src/misc/Time.js index 038623a997b..993129dd0cb 100644 --- a/src/misc/Time.js +++ b/src/misc/Time.js @@ -11,18 +11,18 @@ import angular from 'angular'; export function Time() {} /** - * @param {number|string|null} value The value - * @param {Date} defaultValue The default value - * @return {Date} the date + * @param {?number|string} value The value + * @param {?Date} defaultValue The default value + * @return {?Date} the date */ Time.prototype.createDate = function(value, defaultValue = null) { return value !== null ? new Date(value) : defaultValue; }; /** - * @param {Date} date The date - * @param {number|null=} defaultValue The default value - * @return {number|null} the time + * @param {?Date} date The date + * @param {?number} defaultValue The default value + * @return {?number} the time */ Time.prototype.getTime = function(date, defaultValue = null) { return date ? date.getTime() : defaultValue; @@ -45,14 +45,36 @@ Time.prototype.getOptions = function(time) { const minDefaultDate = this.createDate(time.minDefValue, minDate); const maxDefaultDate = this.createDate(time.maxDefValue, maxDate); + if (!minDefaultDate) { + throw new Error('Missing minDefaultDate'); + } + if (!maxDefaultDate) { + throw new Error('Missing maxDefaultDate'); + } - const defaultValues = (time.mode === 'range') ? - [this.getTime(minDefaultDate), this.getTime(maxDefaultDate)] : - this.getTime(minDefaultDate); + const minTime = this.getTime(minDate); + const maxTime = this.getTime(maxDate); + if (!minTime) { + throw new Error('Missing minTime'); + } + if (!maxTime) { + throw new Error('Missing maxTime'); + } + + const minDefaultTime = this.getTime(minDefaultDate); + const maxDefaultTime = this.getTime(maxDefaultDate); + if (!minDefaultTime) { + throw new Error('Missing minDefaultTime'); + } + if (!maxDefaultTime) { + throw new Error('Missing maxDefaultTime'); + } + + const defaultValues = (time.mode === 'range') ? [minDefaultTime, maxDefaultTime] : minDefaultTime; return { - minDate: this.getTime(minDate), - maxDate: this.getTime(maxDate), + minDate: minTime, + maxDate: maxTime, values: defaultValues }; }; diff --git a/src/misc/btnComponent.js b/src/misc/btnComponent.js index c10c47f666d..7aec1833663 100644 --- a/src/misc/btnComponent.js +++ b/src/misc/btnComponent.js @@ -46,13 +46,16 @@ function buttonGroupComponent($parse) { restrict: 'A', controller: 'ngeoBtnGroupController', /** - * @param {!angular.IScope} scope Scope. - * @param {!JQuery=} element Element. - * @param {!angular.IAttributes=} attrs Attributes. - * @param {!angular.IController=} controller Controller. + * @param {angular.IScope} scope Scope. + * @param {JQuery} element Element. + * @param {angular.IAttributes} attrs Attributes. + * @param {angular.IController=} controller Controller. */ link: (scope, element, attrs, controller) => { - const setActive = $parse(attrs['ngeoBtnGroupActive']).assign; + if (!controller) { + throw new Error('Missing controller'); + } + const setActive = $parse(attrs.ngeoBtnGroupActive).assign; if (setActive) { scope.$watch( @@ -144,12 +147,15 @@ function buttonComponent($parse) { require: ['?^ngeoBtnGroup', 'ngModel'], restrict: 'A', /** - * @param {!angular.IScope} scope Scope. - * @param {!JQuery} element Element. - * @param {!angular.IAttributes} attrs Attributes. - * @param {!angular.IController} ctrls Controller. + * @param {angular.IScope} scope Scope. + * @param {JQuery} element Element. + * @param {angular.IAttributes} attrs Attributes. + * @param {angular.IController|undefined} ctrls Controller. */ link: (scope, element, attrs, ctrls) => { + if (!ctrls) { + throw new Error('Missing ctrls'); + } const buttonsCtrl = ctrls[0]; const ngModelCtrl = ctrls[1]; let indexInGroup = -1; diff --git a/src/misc/datepickerComponent.js b/src/misc/datepickerComponent.js index 5f9cc6f2f4f..a75d4112ce8 100644 --- a/src/misc/datepickerComponent.js +++ b/src/misc/datepickerComponent.js @@ -28,7 +28,7 @@ module.value('ngeoDatePickerTemplateUrl', * @return {string} Template URL. */ (element, attrs) => { - const templateUrl = attrs['ngeoDatePickerTemplateUrl']; + const templateUrl = attrs.ngeoDatePickerTemplateUrl; return templateUrl !== undefined ? templateUrl : 'ngeo/misc/datepickerComponent'; }); @@ -62,10 +62,13 @@ function datePickerComponent(ngeoDatePickerTemplateUrl, $timeout) { restrict: 'AE', templateUrl: ngeoDatePickerTemplateUrl, link: (scope, element, attrs, ctrl) => { + if (!ctrl) { + throw new Error('Missing ctrl'); + } ctrl.init(); const lang = ctrl.gettextCatalog_.getCurrentLanguage(); - $['datepicker']['setDefaults']($['datepicker']['regional'][lang]); + $.datepicker.setDefaults($.datepicker.regional[lang]); ctrl.sdateOptions = angular.extend({}, ctrl.sdateOptions, { 'minDate': ctrl.initialMinDate, @@ -121,15 +124,15 @@ module.directive('ngeoDatePicker', datePickerComponent); function Controller($scope, ngeoTime, gettextCatalog) { /** - * @type {!import("ngeo/misc/Time.js").Time} + * @type {import("ngeo/misc/Time.js").Time} * @private */ this.ngeoTime_ = ngeoTime; /** - * @type {!import('ngeo/datasource/OGC.js').TimeProperty} + * @type {?import('ngeo/datasource/OGC.js').TimeProperty} */ - this.time; + this.time = null; /** * The gettext catalog @@ -143,27 +146,27 @@ function Controller($scope, ngeoTime, gettextCatalog) { * If the component is used to select a date range * @type {boolean} */ - this.isModeRange; + this.isModeRange = false; /** * Function called after date(s) changed/selected - * @type {function({time: {start: number, end: number}}): void} + * @type {?function({time: {start: number, end: ?number}}): void} */ - this.onDateSelected; + this.onDateSelected = null; /** * Initial min date for the datepicker - * @type {!Date} + * @type {?Date} */ - this.initialMinDate; + this.initialMinDate = null; /** * Initial max date for the datepickeronDateSelected - * @type {!Date} + * @type {?Date} */ - this.initialMaxDate; + this.initialMaxDate = null; /** * Datepicker options for the second datepicker (only for range mode) @@ -185,25 +188,33 @@ function Controller($scope, ngeoTime, gettextCatalog) { /** * Start date model for the first date picker - * @type {Date} + * @type {?Date} */ - this.sdate; + this.sdate = null; /** * End date model for the second datepicker (only for range mode) - * @type {Date} + * @type {?Date} */ - this.edate; + this.edate = null; $scope.$watchGroup(['datepickerCtrl.sdate', 'datepickerCtrl.edate'], (newDates, oldDates) => { + if (!this.onDateSelected) { + throw new Error('Missing onDateSelected'); + } const sDate = newDates[0]; const eDate = newDates[1]; if (angular.isDate(sDate) && (!this.isModeRange || angular.isDate(eDate))) { + const start = this.ngeoTime_.getTime(sDate); + const end = this.ngeoTime_.getTime(eDate); + if (!start) { + throw new Error('Missing start'); + } this.onDateSelected({ time: { - start: this.ngeoTime_.getTime(sDate), - end: this.ngeoTime_.getTime(eDate) + start, + end, } }); } @@ -214,7 +225,10 @@ function Controller($scope, ngeoTime, gettextCatalog) { * Initialise the controller. */ Controller.prototype.init = function() { - //fetch the initial options for the component + if (!this.time) { + throw new Error('Missing time'); + } + // Fetch the initial options for the component const initialOptions_ = this.ngeoTime_.getOptions(this.time); this.initialMinDate = this.ngeoTime_.createDate(initialOptions_.minDate); this.initialMaxDate = this.ngeoTime_.createDate(initialOptions_.maxDate); diff --git a/src/misc/decorate.js b/src/misc/decorate.js index 3252a0f0c9b..0e10ff6a306 100644 --- a/src/misc/decorate.js +++ b/src/misc/decorate.js @@ -91,14 +91,14 @@ export function layerLoading(layer, $scope) { let source; /** - * @type {Array|null} + * @type {Array} */ - let incrementEvents = null; + let incrementEvents = []; /** - * @type {Array|null} + * @type {Array} */ - let decrementEvents = null; + let decrementEvents = []; /** * @function diff --git a/src/misc/filereaderComponent.js b/src/misc/filereaderComponent.js index 1e336198678..86a28e83c44 100644 --- a/src/misc/filereaderComponent.js +++ b/src/misc/filereaderComponent.js @@ -65,7 +65,11 @@ function filereaderComponent($window) { scope['fileContent'] = target.result; }); }); - fileReader.readAsText(changeEvent.target.files[0]); + const files = changeEvent.target.files; + if (!files) { + throw new Error('Missing files'); + } + fileReader.readAsText(files[0]); }; element.on({change: ce}); } diff --git a/src/misc/filters.js b/src/misc/filters.js index 0bbea2e0dc6..1f6cfa6cbd0 100644 --- a/src/misc/filters.js +++ b/src/misc/filters.js @@ -562,9 +562,7 @@ module.constant('ngeoStringToHtmlReplacements', StringToHtmlReplacements); */ const removeCDATA = function() { return function(input) { - if (input.replace) { - return input.replace(//, '$1'); - } + return input.replace(//, '$1'); }; }; diff --git a/src/misc/php-date-formatter.js b/src/misc/php-date-formatter.js index 43a2fb6da74..3d4caeb29fe 100644 --- a/src/misc/php-date-formatter.js +++ b/src/misc/php-date-formatter.js @@ -1,3 +1,4 @@ +// @ts-nocheck /* eslint max-len: 0 */ /*! diff --git a/src/olcs/Service.js b/src/olcs/Service.js index 85f590c6849..c4443cfd1a6 100644 --- a/src/olcs/Service.js +++ b/src/olcs/Service.js @@ -23,9 +23,9 @@ export const OlcsService = class { constructor(ngeoDebounce, ngeoLocation, ngeoStateManager) { /** * @private - * @type {import('olcs/contrib/Manager.js').default|undefined} + * @type {?import('olcs/contrib/Manager.js').default} */ - this.manager_; + this.manager_ = null; /** * @private @@ -63,7 +63,7 @@ export const OlcsService = class { } /** - * @return {import('olcs/contrib/Manager.js').default|undefined} the manager. + * @return {?import('olcs/contrib/Manager.js').default} the manager. */ getManager() { return this.manager_; @@ -74,6 +74,9 @@ export const OlcsService = class { * @return {Promise} A promise after load & enabled. */ initialStateToCamera_() { + if (!this.manager_) { + throw new Error('Missing manager'); + } const stateManager = this.ngeoStateManager_; const lon = stateManager.getInitialNumberValue(Permalink3dParam.LON); @@ -82,9 +85,15 @@ export const OlcsService = class { const heading = stateManager.getInitialNumberValue(Permalink3dParam.HEADING) || 0; const pitch = stateManager.getInitialNumberValue(Permalink3dParam.PITCH) || 0; - console.assert(lon !== undefined); - console.assert(lat !== undefined); - console.assert(elevation !== undefined); + if (!lon) { + throw new Error('Missing lon'); + } + if (!lat) { + throw new Error('Missing lat'); + } + if (!elevation) { + throw new Error('Missing elevation'); + } return this.manager_.set3dWithView(lon, lat, elevation, heading, pitch); } @@ -92,6 +101,9 @@ export const OlcsService = class { * @private */ cameraToState_() { + if (!this.manager_) { + throw new Error('Missing manager'); + } const manager = this.manager_; const scene = manager.getOl3d().getCesiumScene(); const camera = scene.camera; diff --git a/src/olcs/controls3d.js b/src/olcs/controls3d.js index c9a9ec78c62..0c8a28900bc 100644 --- a/src/olcs/controls3d.js +++ b/src/olcs/controls3d.js @@ -44,62 +44,62 @@ const Controller = class { this.element_ = $element; /** - * @type {import('olcs/contrib/Manager.js').default} + * @type {?import('olcs/contrib/Manager.js').default} */ - this.ol3dm; + this.ol3dm = null; /** * @type {number} */ - this.minTilt; + this.minTilt = -1; /** * @type {number} * @private */ - this.maxTilt; + this.maxTilt = -1; /** - * @type {JQuery} + * @type {?JQuery} * @private */ - this.tiltRightEl_; + this.tiltRightEl_ = null; /** - * @type {JQuery} + * @type {?JQuery} * @private */ - this.tiltLeftEl_; + this.tiltLeftEl_ = null; /** - * @type {JQuery} + * @type {?JQuery} * @private */ - this.rotation3dEl_; + this.rotation3dEl_ = null; /** - * @type {JQuery} + * @type {?JQuery} * @private */ - this.angle3dEl_; + this.angle3dEl_ = null; /** * @type {number} * @private */ - this.previousRotation_; + this.previousRotation_ = -1; /** - * @type {Cesium.Matrix4} + * @type {?Cesium.Matrix4} * @private */ - this.previousViewMatrix_; + this.previousViewMatrix_ = null; /** * @type {number} * @private */ - this.animationFrameRequestId_; + this.animationFrameRequestId_ = -1; /** * @type {import("ngeo/olcs/Service.js").OlcsService} @@ -109,6 +109,21 @@ const Controller = class { } updateWidget_() { + if (!this.ol3dm) { + throw new Error('Missing ol3dm'); + } + if (!this.rotation3dEl_) { + throw new Error('Missing rotation3dEl_'); + } + if (!this.angle3dEl_) { + throw new Error('Missing angle3dEl_'); + } + if (!this.tiltRightEl_) { + throw new Error('Missing tiltRightEl_'); + } + if (!this.tiltLeftEl_) { + throw new Error('Missing tiltLeftEl_'); + } const newRotation = this.ol3dm.getOl3d().getOlView().getRotation(); if (shouldUpdate(this.previousRotation_, newRotation)) { this.rotateElement_(this.rotation3dEl_, newRotation); @@ -119,7 +134,7 @@ const Controller = class { // @ts-ignore: Cesium if (!Cesium.Matrix4.equalsEpsilon(this.previousViewMatrix_, newViewMatrix, 1e-5)) { const newTilt = this.ol3dm.getTiltOnGlobe(); // this is expensive!! - if (Number.isFinite(newTilt || 0)) { // Workaround https://github.com/google/closure-compiler/pull/2712 + if (newTilt != undefined && Number.isFinite(newTilt || 0)) { this.rotateElement_(this.angle3dEl_, newTilt); // @ts-ignore: Cesium this.previousViewMatrix_ = Cesium.Matrix4.clone(newViewMatrix); @@ -156,7 +171,7 @@ const Controller = class { this.maxTilt = 7 * Math.PI / 16; } if (!this.ol3dm) { - this.ol3dm = this.olcsService_.getManager(); + this.ol3dm = this.olcsService_.getManager() || null; } this.tiltRightEl_ = this.element_.find('.ngeo-tilt-right'); this.tiltLeftEl_ = this.element_.find('.ngeo-tilt-left'); @@ -187,6 +202,9 @@ const Controller = class { * @param {number} angle Angle in degrees. */ rotate(angle) { + if (!this.ol3dm) { + throw new Error('Missing ol3dm'); + } // @ts-ignore: Cesium angle = Cesium.Math.toRadians(angle); this.ol3dm.setHeading(angle); @@ -197,9 +215,15 @@ const Controller = class { * @param {number} angle Angle in degrees. */ tilt(angle) { + if (!this.ol3dm) { + throw new Error('Missing ol3dm'); + } // @ts-ignore: Cesium angle = Cesium.Math.toRadians(angle); const tiltOnGlobe = this.ol3dm.getTiltOnGlobe(); + if (tiltOnGlobe == undefined) { + throw new Error('Missing tiltOnGlobe'); + } if (tiltOnGlobe + angle < this.minTilt) { angle = this.minTilt - tiltOnGlobe; } else if (tiltOnGlobe + angle > this.maxTilt) { @@ -214,6 +238,9 @@ const Controller = class { * @param {number} delta -1 to zoom out and 1 to zoom in. */ zoom(delta) { + if (!this.ol3dm) { + throw new Error('Missing ol3dm'); + } const view = this.ol3dm.getOlView(); const cur = view.getResolution(); const newResolution = view.constrainResolution(cur, delta); diff --git a/src/print/Service.js b/src/print/Service.js index f6ceccc53e2..5f6c5f78a6b 100644 --- a/src/print/Service.js +++ b/src/print/Service.js @@ -174,8 +174,15 @@ PrintService.prototype.encodeMap_ = function(map, scale, object) { const viewResolution = view.getResolution(); const viewRotation = object.rotation || toDegrees(view.getRotation()); - console.assert(viewCenter !== undefined); - console.assert(viewProjection !== undefined); + if (!viewCenter) { + throw new Error('Missing viewCenter'); + } + if (!viewProjection) { + throw new Error('Missing viewProjection'); + } + if (!viewResolution) { + throw new Error('Missing viewResolution'); + } object.center = viewCenter; object.projection = viewProjection.getCode(); @@ -184,7 +191,9 @@ PrintService.prototype.encodeMap_ = function(map, scale, object) { object.layers = []; const mapLayerGroup = map.getLayerGroup(); - console.assert(mapLayerGroup); + if (!mapLayerGroup) { + throw new Error('Missing mapLayerGroup'); + } this.printNativeAngle_ = !(mapLayerGroup.get('printNativeAngle') === false); let layers = this.ngeoLayerHelper_.getFlatLayers(mapLayerGroup); @@ -194,7 +203,6 @@ PrintService.prototype.encodeMap_ = function(map, scale, object) { layers.forEach((layer) => { if (layer.getVisible()) { - console.assert(viewResolution !== undefined); this.encodeLayer(object.layers, layer, viewResolution); } }); @@ -231,7 +239,9 @@ PrintService.prototype.encodeVectorLayer = function(arr, layer, resolution) { * @private */ PrintService.prototype.encodeImageLayer_ = function(arr, layer) { - console.assert(layer instanceof olLayerImage); + if (!(layer instanceof olLayerImage)) { + throw new Error('layer not instance of olLayerImage'); + } const source = layer.getSource(); if (source instanceof olSourceImageWMS) { this.encodeImageWmsLayer_(arr, layer); @@ -245,10 +255,13 @@ PrintService.prototype.encodeImageLayer_ = function(arr, layer) { * @private */ PrintService.prototype.encodeImageWmsLayer_ = function(arr, layer) { - const source = /** @type {olSourceImageWMS} */(layer.getSource()); - - console.assert(layer instanceof olLayerImage); - console.assert(source instanceof olSourceImageWMS); + if (!(layer instanceof olLayerImage)) { + throw new Error('layer not instance of olLayerImage'); + } + const source = layer.getSource(); + if (!(source instanceof olSourceImageWMS)) { + throw new Error('source not instance of olSourceImageWMS'); + } const url = source.getUrl(); if (url !== undefined) { @@ -323,7 +336,9 @@ function getAbsoluteUrl_(url) { * @private */ PrintService.prototype.encodeTileLayer_ = function(arr, layer) { - console.assert(layer instanceof olLayerTile); + if (!(layer instanceof olLayerTile)) { + throw new Error('layer not instance of olLayerTile'); + } const source = layer.getSource(); if (source instanceof olSourceWMTS) { this.encodeTileWmtsLayer_(arr, layer); @@ -339,13 +354,26 @@ PrintService.prototype.encodeTileLayer_ = function(arr, layer) { * @private */ PrintService.prototype.encodeTileWmtsLayer_ = function(arr, layer) { - console.assert(layer instanceof olLayerTile); - const source = /** @type {olSourceWMTS} */(layer.getSource()); - console.assert(source instanceof olSourceWMTS); + if (!(layer instanceof olLayerTile)) { + throw new Error('layer not instance of olLayerTile'); + } + const source = layer.getSource(); + if (!(source instanceof olSourceWMTS)) { + throw new Error('source not instance of olSourceWMTS'); + } const projection = source.getProjection(); - const tileGrid = /** @type {olTilegridWMTS} */(source.getTileGrid()); - console.assert(tileGrid instanceof olTilegridWMTS); + if (!projection) { + throw new Error('Missing projection'); + } + const metersPerUnit = projection.getMetersPerUnit(); + if (!metersPerUnit) { + throw new Error('Missing metersPerUnit'); + } + const tileGrid = source.getTileGrid(); + if (!(tileGrid instanceof olTilegridWMTS)) { + throw new Error('tileGrid not instance of olTilegridWMTS'); + } const matrixIds = tileGrid.getMatrixIds(); /** @type {Array.} */ @@ -355,8 +383,7 @@ PrintService.prototype.encodeTileWmtsLayer_ = function(arr, layer) { const tileRange = tileGrid.getFullTileRange(i); matrices.push(/** @type {import('ngeo/print/mapfish-print-v3.js').MapFishPrintWmtsMatrix} */ ({ identifier: matrixIds[i], - scaleDenominator: tileGrid.getResolution(i) * - projection.getMetersPerUnit() / 0.28E-3, + scaleDenominator: tileGrid.getResolution(i) * metersPerUnit / 0.28E-3, tileSize: olSize.toSize(tileGrid.getTileSize(i)), topLeftCorner: tileGrid.getOrigin(i), matrixSize: [ @@ -394,13 +421,19 @@ PrintService.prototype.encodeTileWmtsLayer_ = function(arr, layer) { * @private */ PrintService.prototype.encodeTileWmsLayer_ = function(arr, layer) { - const source = /** @type {olSourceTileWMS} */(layer.getSource()); - - console.assert(layer instanceof olLayerTile); - console.assert(source instanceof olSourceTileWMS); + if (!(layer instanceof olLayerTile)) { + throw new Error('layer not instance of olLayerTile'); + } + const source = layer.getSource(); + if (!(source instanceof olSourceTileWMS)) { + throw new Error('source not instance of olSourceTileWMS'); + } - this.encodeWmsLayer_( - arr, layer, source.getUrls()[0], source.getParams()); + const urls = source.getUrls(); + if (!urls) { + throw new Error('Missing urls'); + } + this.encodeWmsLayer_(arr, layer, urls[0], source.getParams()); }; @@ -412,7 +445,9 @@ PrintService.prototype.encodeTileWmsLayer_ = function(arr, layer) { */ PrintService.prototype.getWmtsUrl_ = function(source) { const urls = source.getUrls(); - console.assert(urls.length > 0); + if (!urls) { + throw new Error('Missing urls'); + } return getAbsoluteUrl_(urls[0]); }; diff --git a/src/print/Utils.js b/src/print/Utils.js index d6c9d40e67f..3dbd6a74e59 100644 --- a/src/print/Utils.js +++ b/src/print/Utils.js @@ -17,13 +17,13 @@ export function PrintUtils() { * @type {number} * @private */ - this.extentHalfHorizontalDistance_; + this.extentHalfHorizontalDistance_ = -1; /** * @type {number} * @private */ - this.extentHalfVerticalDistance_; + this.extentHalfVerticalDistance_ = -1; } @@ -63,7 +63,13 @@ PrintUtils.prototype.createPrintMaskPostcompose = function(getSize, getScale, op function(evt) { if (evt instanceof RenderEvent) { const context = evt.context; + if (!context) { + throw new Error('Missing context'); + } const frameState = evt.frameState; + if (!frameState) { + throw new Error('Missing state'); + } const resolution = frameState.viewState.resolution; @@ -80,15 +86,11 @@ PrintUtils.prototype.createPrintMaskPostcompose = function(getSize, getScale, op const ppi = DOTS_PER_INCH; const ipm = INCHES_PER_METER; - const extentHalfWidth = - (((width / ppi) / ipm) * scale / resolution) / 2; - self.extentHalfHorizontalDistance_ = - (((size[0] / ppi) / ipm) * scale) / 2; + const extentHalfWidth = (((width / ppi) / ipm) * scale / resolution) / 2; + self.extentHalfHorizontalDistance_ = (((size[0] / ppi) / ipm) * scale) / 2; - const extentHalfHeight = - (((height / ppi) / ipm) * scale / resolution) / 2; - self.extentHalfVerticalDistance_ = - (((size[1] / ppi) / ipm) * scale) / 2; + const extentHalfHeight = (((height / ppi) / ipm) * scale / resolution) / 2; + self.extentHalfVerticalDistance_ = (((size[1] / ppi) / ipm) * scale) / 2; // Draw a mask on the whole map. context.beginPath(); diff --git a/src/print/VectorEncoder.js b/src/print/VectorEncoder.js index 966e1113038..50d978c09cd 100644 --- a/src/print/VectorEncoder.js +++ b/src/print/VectorEncoder.js @@ -78,7 +78,7 @@ VectorEncoder.prototype.encodeVectorLayer = function(arr, layer, resolution) { } const origGeojsonFeature = this.geojsonFormat.writeFeatureObject(originalFeature); /** - * @type {Array} + * @type {?Array} */ const styles = (styleData !== null && !Array.isArray(styleData)) ? [styleData] : styleData; console.assert(Array.isArray(styles)); @@ -91,7 +91,7 @@ VectorEncoder.prototype.encodeVectorLayer = function(arr, layer, resolution) { let geojsonFeature; if (!geometry) { geojsonFeature = origGeojsonFeature; - geometry = originalFeature.getGeometry(); + geometry = /** @type {import("ol/geom/Geometry.js").default} */(originalFeature.getGeometry()); // no need to encode features with no geometry if (!geometry) { continue; @@ -101,11 +101,10 @@ VectorEncoder.prototype.encodeVectorLayer = function(arr, layer, resolution) { isOriginalFeatureAdded = true; } } else { - let styledFeature = originalFeature.clone(); + const styledFeature = originalFeature.clone(); styledFeature.setGeometry(geometry); geojsonFeature = this.geojsonFormat.writeFeatureObject(styledFeature); - geometry = styledFeature.getGeometry(); - styledFeature = null; + geometry = /** @type {import("ol/geom/Geometry.js").default} */(styledFeature.getGeometry()); geojsonFeatures.push(geojsonFeature); } @@ -244,7 +243,11 @@ VectorEncoder.prototype.encodeVectorStylePoint = function(symbolizers, imageStyl this.encodeVectorStyleStroke(symbolizer, strokeStyle); } } else if (imageStyle instanceof olStyleIcon) { - const src = new URL(imageStyle.getSrc(), window.location.href).href; + const imgSrc = imageStyle.getSrc(); + if (!imgSrc) { + throw new Error('Missing imgSrc'); + } + const src = new URL(imgSrc, window.location.href).href; if (src !== undefined) { symbolizer = /** @type {import('ngeo/print/mapfish-print-v3.js').MapFishPrintSymbolizerPoint} */ ({ type: 'point', @@ -363,7 +366,7 @@ VectorEncoder.prototype.encodeVectorStyleStroke = function(symbolizer, strokeSty const strokeLineCap = strokeStyle.getLineCap(); if (strokeLineCap) { /** @type {import('ngeo/print/mapfish-print-v3.js').MapFishPrintSymbolizerLine} */(symbolizer) - .strokeLinecap = strokeStyle.getLineCap(); + .strokeLinecap = strokeLineCap; } }; diff --git a/src/profile/d3Elevation.js b/src/profile/d3Elevation.js index bb3afca263d..a231f613ce5 100644 --- a/src/profile/d3Elevation.js +++ b/src/profile/d3Elevation.js @@ -271,7 +271,8 @@ function d3Elevation(options) { /** @type {import('d3').Axis} */ const yAxis = d3axisLeft(y); - let area; + /** @type {?d3.Area<[number, number]>} */ + let area = null; if (numberOfLines === 1) { area = d3area() .x(d => x(distanceExtractor(d))) @@ -358,7 +359,7 @@ function d3Elevation(options) { .attr('transform', `translate(${margin.left},${ margin.top})`); - xDomain = d3extent(data, d => distanceExtractor(d)); + xDomain = /** @type{[number, number]} */(d3extent(data, d => distanceExtractor(d))); x.domain(xDomain); // Return an array with the min and max value of the min/max values of @@ -368,7 +369,8 @@ function d3Elevation(options) { // Get min/max values (extent) of each lines. for (const name in linesConfiguration) { /** @type {[number, number]} */ - const extent = d3extent(data, d => linesConfiguration[name].zExtractor(d)); + const extent = /** @type{[number, number]} */( + d3extent(data, d => linesConfiguration[name].zExtractor(d))); // only include defined extent if (extent.every(Number.isFinite)) { elevationsValues = elevationsValues.concat(extent); @@ -393,6 +395,9 @@ function d3Elevation(options) { // Update the area path. if (numberOfLines === 1) { + if (!area) { + throw new Error('Missing area'); + } g.select('.area') .transition() .attr('d', area); @@ -528,6 +533,7 @@ function d3Elevation(options) { const dist = distanceExtractor(point); let elevation; const elevations = []; + /** @type {Object} */ const elevationsRef = {}; let lineName; diff --git a/src/query/MapQuerent.js b/src/query/MapQuerent.js index 0035771546f..959c77f9853 100644 --- a/src/query/MapQuerent.js +++ b/src/query/MapQuerent.js @@ -197,7 +197,7 @@ export class MapQuerent { for (const source of this.result_.sources) { if (!keep) { source.features.length = 0; - source.totalFeatureCount = undefined; + delete source.totalFeatureCount; } else { this.result_.total += source.features.length; } @@ -226,6 +226,9 @@ export class MapQuerent { for (const idStr in response) { const id = Number(idStr); const dataSource = this.ngeoDataSourcesHelper_.getDataSource(id); + if (!dataSource) { + throw new Error('Missing dataSource'); + } let label = dataSource.name; console.assert(dataSource); diff --git a/src/query/Querent.js b/src/query/Querent.js index aad1f1ff2e8..56fec9beec1 100644 --- a/src/query/Querent.js +++ b/src/query/Querent.js @@ -18,17 +18,17 @@ import olSourceImageWMS from 'ol/source/ImageWMS.js'; * `wfs` list. * * @typedef {Object} QueryableDataSources - * @property {!Array.} wms List of queryable data sources that support WMS. - * @property {!Array.} wfs List of queryable data sources that support WFS. + * @property {Array} wms List of queryable data sources that support WMS. + * @property {Array} wfs List of queryable data sources that support WFS. */ /** * @typedef {Object} QuerentResultItem - * @property {!Array.} features + * @property {Array} features * @property {number} limit - * @property {boolean} tooManyFeatures - * @property {number} totalFeatureCount + * @property {boolean} [tooManyFeatures] + * @property {number} [totalFeatureCount] */ /** * Hash of features by data source ids. @@ -49,7 +49,7 @@ import olSourceImageWMS from 'ol/source/ImageWMS.js'; * - `remove`: newly queried features are removed from the existing ones * @property {import("ol/coordinate.js").Coordinate} [coordinate] The coordinate to issue the requests with, * which can end up with either WMS or WFS requests. - * @property {Array.} [dataSources] ist of data sources to + * @property {Array} [dataSources] ist of data sources to * query. Only those that meet the requirements will actually be queried. The querent service requires * either the `dataSources` or `queryableDataSources` property to be set. * @property {import("ol/extent.js").Extent} [extent] The extent to issue the requests with, which can end up @@ -95,8 +95,8 @@ export class Querent { * * @param {angular.IHttpService} $http Angular $http service. * @param {angular.IQService} $q The Angular $q service. - * @param {!import("ngeo/filter/RuleHelper.js").RuleHelper} ngeoRuleHelper Ngeo rule helper service. - * @param {!import("ngeo/misc/WMSTime.js").WMSTime} ngeoWMSTime wms time service. + * @param {import("ngeo/filter/RuleHelper.js").RuleHelper} ngeoRuleHelper Ngeo rule helper service. + * @param {import("ngeo/misc/WMSTime.js").WMSTime} ngeoWMSTime wms time service. * @ngdoc service * @ngname ngeoQuerent * @ngInject @@ -118,13 +118,13 @@ export class Querent { this.q_ = $q; /** - * @type {!import("ngeo/filter/RuleHelper.js").RuleHelper} + * @type {import("ngeo/filter/RuleHelper.js").RuleHelper} * @private */ this.ngeoRuleHelper_ = ngeoRuleHelper; /** - * @type {!import("ngeo/misc/WMSTime.js").WMSTime} + * @type {import("ngeo/misc/WMSTime.js").WMSTime} * @private */ this.ngeoWMSTime_ = ngeoWMSTime; @@ -134,7 +134,7 @@ export class Querent { /** * Promises that can be resolved to cancel started requests. - * @type {!Array.} + * @type {Array} * @private */ this.requestCancelers_ = []; @@ -142,7 +142,7 @@ export class Querent { /** * Cache of promises for WMS GetCapabilities requests. They key is the * online resource base url that is used to do the query. - * @type {!Object.} + * @type {Object} * @private */ this.wmsGetCapabilitiesPromises_ = {}; @@ -150,7 +150,7 @@ export class Querent { /** * Cache of promises for WMST GetCapabilities requests. They key is the * url that is used to do the query. - * @type {!Object.} + * @type {Object} * @private */ this.wmtsGetCapabilitiesPromises_ = {}; @@ -213,17 +213,20 @@ export class Querent { * * The map view resolution determines if the inner ogc layers are in range. * - * @param {!Array.} dataSources Data sources + * @param {Array} dataSources Data sources * @param {import("ol/Map.js").default} map Map. - * @return {!QueryableDataSources} Queryable data sources. + * @return {QueryableDataSources} Queryable data sources. */ getQueryableDataSources(dataSources, map) { const queryableDataSources = { - wfs: [], - wms: [] + wfs: /** @type{Array} */([]), + wms: /** @type{Array} */([]), }; const resolution = map.getView().getResolution(); + if (!resolution) { + throw new Error('Missing resolution'); + } for (const dataSource of dataSources) { @@ -250,12 +253,13 @@ export class Querent { * @return {angular.IPromise} Promise. */ wfsDescribeFeatureType(dataSource) { - - console.assert( - dataSource.supportsAttributes, - `The data source must support WFS, have a single OGCLayer that - is queryable in order to issue WFS DescribeFeatureType requests` - ); + if (!dataSource.supportsAttributes) { + throw `The data source must support WFS, have a single OGCLayer that + is queryable in order to issue WFS DescribeFeatureType requests`; + } + if (!dataSource.wfsUrl) { + throw new Error('Missing WFS URL'); + } const ogcLayerNames = dataSource.getOGCLayerNames(); @@ -282,12 +286,12 @@ export class Querent { let found = null; for (const layerCapability of layerCapabilities) { - if (layerCapability['Name'] === layerName) { + if (layerCapability.Name === layerName) { found = layerCapability; break; - } else if (layerCapability['Layer']) { + } else if (layerCapability.Layer) { found = this.wmsFindLayerCapability( - layerCapability['Layer'], layerName); + layerCapability.Layer, layerName); if (found) { break; } @@ -326,7 +330,9 @@ export class Querent { promise = this.wmsGetCapabilitiesPromises_[baseUrl]; } - console.assert(promise); + if (!promise) { + throw new Error('Missing promise'); + } if (cache && !this.wmsGetCapabilitiesPromises_[baseUrl]) { this.wmsGetCapabilitiesPromises_[baseUrl] = promise; @@ -343,7 +349,7 @@ export class Querent { wmtsFindLayerCapability(layerCapabilities, layerName) { let found = null; for (const layerCapability of layerCapabilities) { - if (layerCapability['Identifier'] === layerName) { + if (layerCapability.Identifier === layerName) { found = layerCapability; break; } @@ -373,7 +379,9 @@ export class Querent { promise = this.wmtsGetCapabilitiesPromises_[url]; } - console.assert(promise); + if (!promise) { + throw new Error('Missing promise'); + } if (cache && !this.wmtsGetCapabilitiesPromises_[url]) { this.wmtsGetCapabilitiesPromises_[url] = promise; @@ -431,29 +439,29 @@ export class Querent { const hash = {}; for (const dataSource of dataSources) { - let features; - let tooManyFeatures; - let totalFeatureCount; + + const dataSourceId = dataSource.id; if (typeof response === 'number') { - features = []; - tooManyFeatures = true; - totalFeatureCount = response; + const features = []; + const tooManyFeatures = true; + const totalFeatureCount = response; + this.setUniqueIds_(features, dataSource.id); + hash[dataSourceId] = { + features, + limit, + tooManyFeatures, + totalFeatureCount + }; } else { - if (dataSource instanceof ngeoDatasourceOGC) { - features = this.readAndTypeFeatures_(dataSource, response.data, wfs); - } else { - features = []; - } + const features = dataSource instanceof ngeoDatasourceOGC ? + this.readAndTypeFeatures_(dataSource, response.data, wfs) : []; + this.setUniqueIds_(features, dataSource.id); + hash[dataSourceId] = { + features, + limit, + }; } - const dataSourceId = dataSource.id; - this.setUniqueIds_(features, dataSource.id); - hash[dataSourceId] = { - features, - limit, - tooManyFeatures, - totalFeatureCount - }; } return hash; @@ -478,8 +486,14 @@ export class Querent { // Assign temporarily a single feature type to read features separately. this.getSetOlFormatTypes_(dataSource, wfs, [type]); if (wfs) { + if (!dataSource.wfsFormat) { + throw new Error('Missing wfsFormat'); + } readFeatures = dataSource.wfsFormat.readFeatures(data); } else { + if (!dataSource.wmsFormat) { + throw new Error('Missing wmsFormat'); + } readFeatures = dataSource.wmsFormat.readFeatures(data); } if (readFeatures.length > 0) { @@ -508,11 +522,17 @@ export class Querent { getSetOlFormatTypes_(dataSource, wfs, opt_types) { let types; if (wfs) { + if (!dataSource.wfsFormat) { + throw new Error('Missing wfsFormat'); + } if (opt_types) { dataSource.wfsFormat.setFeatureType(opt_types); } types = dataSource.wfsFormat.getFeatureType(); } else { + if (!dataSource.wmsFormat) { + throw new Error('Missing wmsFormat'); + } if (opt_types) { dataSource.wmsFormat.setLayers(opt_types); } @@ -546,6 +566,9 @@ export class Querent { const projection = view.getProjection(); const srsName = projection.getCode(); const wfsCount = options.wfsCount === true; + if (!resolution) { + throw new Error('Missing resolution'); + } // (1) Extent (bbox), which is optional, i.e. its value can stay undefined let bbox; @@ -566,9 +589,8 @@ export class Querent { const wfsFormat = new olFormatWFS(); const xmlSerializer = new XMLSerializer(); for (const dataSources of combinedDataSources) { - /** @type {import('ol/format/WFS.js').WriteGetFeatureOptions} */ - let getFeatureCommonOptions; - let featureNS; + let getFeatureCommonOptions = null; + /** @type{Array} */ let featureTypes = []; let url; const params = {}; @@ -582,7 +604,7 @@ export class Querent { // (a) Create common options, if not done yet if (!getFeatureCommonOptions) { - featureNS = dataSource.wfsFeatureNS; + const featureNS = dataSource.wfsFeatureNS; const featurePrefix = dataSource.wfsFeaturePrefix; const geometryName = dataSource.geometryName; const outputFormat = dataSource.wfsOutputFormat; @@ -594,7 +616,8 @@ export class Querent { geometryName, outputFormat, srsName, - featureTypes: null + filter: /** @type{?import("ol/format/filter/Filter.js").default} */(null), + featureTypes: /** @type{?Array} */(null), }; url = dataSource.wfsUrl; @@ -642,9 +665,13 @@ export class Querent { } } - console.assert(getFeatureCommonOptions); + if (!getFeatureCommonOptions) { + throw new Error('Missing getFeatureCommonOptions'); + } getFeatureCommonOptions.featureTypes = featureTypes; - console.assert(url); + if (!url) { + throw new Error('Missing url'); + } // (4) Build query then launch // @@ -672,7 +699,7 @@ export class Querent { { resultType: 'hits' }, - getFeatureCommonOptions + /** @type {import('ol/format/WFS.js').WriteGetFeatureOptions} */(getFeatureCommonOptions) ); const featureCountXml = wfsFormat.writeGetFeature(getCountOptions); const featureCountRequest = xmlSerializer.serializeToString( @@ -687,10 +714,16 @@ export class Querent { timeout: canceler.promise } ).then((response) => { + if (!dataSources[0].wfsFormat) { + throw new Error('Missing wfsFormat'); + } const meta = dataSources[0].wfsFormat.readFeatureCollectionMetadata( response.data ); - return meta['numberOfFeatures']; + if (!meta) { + throw new Error('Missing meta'); + } + return meta.numberOfFeatures; }); } else { countPromise = this.q_.resolve(); @@ -699,7 +732,7 @@ export class Querent { // (4.2) After count, do GetFeature (if required) /** * @param {number|void} numberOfFeatures value - * @returns {angular.IPromise} undefined + * @returns {?angular.IPromise} */ const afterCount_ = (numberOfFeatures) => { // `true` is returned if a count request was made AND there would @@ -734,8 +767,7 @@ export class Querent { } else { getFeatureDefer.resolve(numberOfFeatures); } - - return undefined; + return null; }; countPromise.then(afterCount_); } @@ -766,10 +798,15 @@ export class Querent { const resolution = view.getResolution(); const projection = view.getProjection(); const projCode = projection.getCode(); + if (!resolution) { + throw new Error('Missing resolution'); + } // (1) Coordinate, which is required to issue WMS GetFeatureInfo requests const coordinate = options.coordinate; - console.assert(coordinate); + if (!coordinate) { + throw new Error('Missing coordinate'); + } // (2) Launch one request per combinaison of data sources for (const dataSources of combinedDataSources) { @@ -824,7 +861,7 @@ export class Querent { console.assert(dataSources.length === 1); params['TIME'] = this.ngeoWMSTime_.formatWMSTimeParam( dataSource.timeProperty, - /** @type {{start : number, end : (number|undefined)}} */ (dataSource.timeRangeValue), + /** @type {import('ngeo/datasource/OGC.js').TimeRange} */ (dataSource.timeRangeValue), ); } } @@ -856,7 +893,9 @@ export class Querent { params['FILTER'] = filterParamValue; } - console.assert(url); + if (!url) { + throw new Error('Missing url'); + } const wmsSource = new olSourceImageWMS({ params, url, @@ -871,6 +910,9 @@ export class Querent { 'INFO_FORMAT': INFO_FORMAT } ); + if (!wmsGetFeatureInfoUrl) { + throw new Error('Missing wmsGetFeatureInfoUrl'); + } const canceler = this.registerCanceler_(); promises.push( diff --git a/src/routing/NominatimInputComponent.js b/src/routing/NominatimInputComponent.js index e521189cd92..5d6b195d362 100644 --- a/src/routing/NominatimInputComponent.js +++ b/src/routing/NominatimInputComponent.js @@ -79,14 +79,14 @@ function Controller($element, $scope, ngeoNominatimService) { this.ngeoNominatimService = ngeoNominatimService; /** - * @type {(function(Object): void|undefined)} + * @type {?function(Object): void} */ - this.onSelect; + this.onSelect = null; /** - * @type {string} + * @type {?string} */ - this.inputValue; + this.inputValue = null; /** * @type {Twitter.Typeahead.Options} diff --git a/src/routing/RoutingComponent.js b/src/routing/RoutingComponent.js index 3b480c64b9c..7e0fe64ca37 100644 --- a/src/routing/RoutingComponent.js +++ b/src/routing/RoutingComponent.js @@ -18,8 +18,8 @@ import 'ngeo/sass/font.scss'; /** * @typedef {Object} RoutingVia - * @property {?import("ol/Feature.js").default} feature - * @property {function(import('ngeo/routing/NominatimService').NominatimSearchResult)} onSelect + * @property {import("ol/Feature.js").default} [feature] + * @property {function(import('ngeo/routing/NominatimService').NominatimSearchResult)} [onSelect] */ @@ -141,9 +141,9 @@ class Controller { this.$q_ = $q; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} */ - this.map; + this.map = null; /** * @type {string} @@ -151,17 +151,17 @@ class Controller { this.errorMessage = ''; /** - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ this.startFeature_ = null; /** - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ this.targetFeature_ = null; /** - * @type {Array.} + * @type {Array} */ this.viaArray = []; @@ -221,7 +221,7 @@ class Controller { this.regexIsFormattedCoord = /\d+\.\d+\/\d+\.\d+/; /** - * @type {import("ol/interaction/Draw.js").default} + * @type {?import("ol/interaction/Draw.js").default} * @private */ this.draw_ = null; @@ -240,7 +240,9 @@ class Controller { * Init the controller */ $onInit() { - this.map.addLayer(this.routeLayer_); + if (this.map) { + this.map.addLayer(this.routeLayer_); + } } /** @@ -259,10 +261,13 @@ class Controller { /** * Converts feature point into LonLat coordinate. * @param {import("ol/Feature.js").default} point Feature point to convert - * @return {import("ol/coordinate.js").Coordinate} LonLat coordinate + * @return {?import("ol/coordinate.js").Coordinate} LonLat coordinate * @private */ getLonLatFromPoint_(point) { + if (!this.map) { + return null; + } const geometry = /** @type {import("ol/geom/Point.js").default} */ (point.getGeometry()); const coords = geometry.getCoordinates(); const projection = this.map.getView().getProjection(); @@ -290,6 +295,9 @@ class Controller { * @private */ parseRoute_(route) { + if (!this.map) { + return []; + } let parsedRoutes = []; const format = new olFormatGeoJSON(); const formatConfig = { @@ -322,9 +330,12 @@ class Controller { const vias = this.viaArray.filter(via => via.feature !== null).map( via => this.getLonLatFromPoint_(via.feature) ); - const route = [coordFrom].concat(vias, [coordTo]); + const route = /** @type Array> */([coordFrom].concat(vias, [coordTo])); const onSuccess_ = (resp) => { + if (!this.map || !this.startFeature_ || !this.targetFeature_) { + return null; + } const features = this.parseRoute_(resp.data.routes[0]); if (features.length === 0) { console.log('No route or not supported format.'); @@ -368,29 +379,25 @@ class Controller { }; const options = {}; - options['steps'] = true; - options['overview'] = false; - options['geometries'] = 'geojson'; + options.steps = true; + options.overview = false; + options.geometries = 'geojson'; const config = {}; - config['options'] = options; + config.options = options; if (this.selectedRoutingProfile) { - config['instance'] = this.selectedRoutingProfile['profile']; + config.instance = this.selectedRoutingProfile.profile; } - this.$q_.when(this.ngeoRoutingService_.getRoute(route, config)) - .then(onSuccess_, onError_); + this.$q_.when(this.ngeoRoutingService_.getRoute(route, config)).then(onSuccess_, onError_); } } /** */ addVia() { - this.viaArray.push(/** @type{RoutingVia} */({ - feature: null, - onSelect: null - })); + this.viaArray.push({}); } /** diff --git a/src/routing/RoutingFeatureComponent.js b/src/routing/RoutingFeatureComponent.js index 8b14ae269fb..15c85689250 100644 --- a/src/routing/RoutingFeatureComponent.js +++ b/src/routing/RoutingFeatureComponent.js @@ -98,15 +98,15 @@ class Controller { this.ngeoNominatimService_ = ngeoNominatimService; /** - * @type {import("ol/Map.js").default} + * @type {?import("ol/Map.js").default} * @private */ - this.map; + this.map = null; /** - * @type {import("ol/Feature.js").default} + * @type {?import("ol/Feature.js").default} */ - this.feature; + this.feature = null; /** * @type {string} @@ -116,17 +116,17 @@ class Controller { /** * @type {string} */ - this.fillColor; + this.fillColor = ''; /** * @type {string} */ - this.strokeColor; + this.strokeColor = ''; /** - * @type {function(import("ol/Feature.js").default): void} + * @type {?function(import("ol/Feature.js").default): void} */ - this.onChange; + this.onChange = null; /** * @type {import("ol/Collection.js").default} @@ -176,7 +176,7 @@ class Controller { }); /** - * @type {import("ol/interaction/Draw.js").default} + * @type {?import("ol/interaction/Draw.js").default} * @private */ this.draw_ = null; @@ -193,6 +193,9 @@ class Controller { } $onInit() { + if (!this.map) { + return; + } this.map.addLayer(this.vectorLayer_); // setup modify interaction @@ -223,6 +226,9 @@ class Controller { * Cleanup, mostly relevant for vias. */ $onDestroy() { + if (!this.map) { + return; + } this.map.removeLayer(this.vectorLayer_); this.modifyFeature_.setActive(false); this.map.removeInteraction(this.modifyFeature_); @@ -231,6 +237,9 @@ class Controller { /** */ set() { + if (!this.map) { + return; + } if (this.draw_) { this.map.removeInteraction(this.draw_); } @@ -247,7 +256,7 @@ class Controller { }); this.draw_.on('drawend', (event) => { - if (this.draw_) { + if (this.draw_ && this.map) { this.map.removeInteraction(this.draw_); } this.snapFeature_(event.feature); @@ -264,6 +273,9 @@ class Controller { * @private */ setFeature_(coordinate, label) { + if (!this.map) { + return; + } const transformedCoordinate = olProj.fromLonLat(coordinate, this.map.getView().getProjection()); if (label === '') { label = transformedCoordinate.join('/'); @@ -275,17 +287,22 @@ class Controller { } onFeatureChange_() { - // update label + if (!this.feature) { + return; + } + // Update label this.featureLabel = /** @type{string} */(this.feature.get('name') || ''); - //update vector source + // Update vector source this.vectorSource_.clear(); this.vectorSource_.addFeature(this.feature); - // notify others + // Notify others if (this.onChange) { this.timeout_(() => { - this.onChange(this.feature); + if (this.feature && this.onChange) { + this.onChange(this.feature); + } }); } } @@ -295,6 +312,9 @@ class Controller { * @private */ onSelect_(selected) { + if (!this.feature || !this.map) { + return; + } // @ts-ignore: If types are not respected const coordinate = selected.coordinate.map(parseFloat); const label = selected.label; @@ -312,6 +332,9 @@ class Controller { */ snapFeature_(feature) { const coord = this.getLonLatFromPoint_(feature); + if (!coord) { + return; + } const config = {}; const onSuccess = (resp) => { @@ -334,10 +357,13 @@ class Controller { /** * Converts feature point into LonLat coordinate. * @param {import("ol/Feature.js").default} point Feature point to convert - * @return {import("ol/coordinate.js").Coordinate} LonLat coordinate + * @return {?import("ol/coordinate.js").Coordinate} LonLat coordinate * @private */ getLonLatFromPoint_(point) { + if (!this.map) { + return null; + } const geometry = /** @type {import("ol/geom/Point.js").default} */ (point.getGeometry()); const coords = geometry.getCoordinates(); const projection = this.map.getView().getProjection(); diff --git a/src/rule/Geometry.js b/src/rule/Geometry.js index d0143ef8527..0d4c6bb6ff1 100644 --- a/src/rule/Geometry.js +++ b/src/rule/Geometry.js @@ -124,7 +124,7 @@ export default class extends ngeoRuleRule { * @param {?import("ol/geom/Geometry.js").default} geometry Geometry */ set geometry(geometry) { - this.feature_.setGeometry(geometry); + this.feature_.setGeometry(geometry || undefined); } // === Other methods === diff --git a/src/search/createGeoJSONBloodhound.js b/src/search/createGeoJSONBloodhound.js index 75fee4fa4ae..5b6a7fc7079 100644 --- a/src/search/createGeoJSONBloodhound.js +++ b/src/search/createGeoJSONBloodhound.js @@ -26,7 +26,9 @@ export function createGeoJSONBloodhound(url, opt_filter, opt_featureProjection, remote: { url, prepare(query, settings) { - settings.url = settings.url.replace('%QUERY', query); + if (settings.url) { + settings.url = settings.url.replace('%QUERY', query); + } return settings; }, transform(parsedResponse) { diff --git a/src/search/createLocationSearchBloodhound.js b/src/search/createLocationSearchBloodhound.js index fe2a70233d3..65f537a5742 100644 --- a/src/search/createLocationSearchBloodhound.js +++ b/src/search/createLocationSearchBloodhound.js @@ -75,12 +75,14 @@ function createLocationSearchBloodhound(opt_options) { remote: { url: 'https://api3.geo.admin.ch/rest/services/api/SearchServer?type=locations&searchText=%QUERY', prepare: (query, settings) => { - settings.url = settings.url.replace('%QUERY', query); - if (options.limit !== undefined) { - settings.url += `&limit=${options.limit}`; - } - if (options.origins !== undefined) { - settings.url += `&origins=${options.origins}`; + if (settings.url) { + settings.url = settings.url.replace('%QUERY', query); + if (options.limit !== undefined) { + settings.url += `&limit=${options.limit}`; + } + if (options.origins !== undefined) { + settings.url += `&origins=${options.origins}`; + } } return (options.prepare !== undefined) ? diff --git a/src/statemanager/Service.js b/src/statemanager/Service.js index b3968ceda12..0b61db74c04 100644 --- a/src/statemanager/Service.js +++ b/src/statemanager/Service.js @@ -4,182 +4,182 @@ import ngeoStatemanagerLocation from 'ngeo/statemanager/Location.js'; /** * Provides a service for managing the application state. * The application state is written to both the URL and the local storage. - * @constructor - * @param {!import("ngeo/statemanager/Location.js").StatemanagerLocation} ngeoLocation ngeo location service. - * @param {!Array.} ngeoUsedKeyRegexp regexp used to identify the used keys. - * @ngInject * @hidden */ -export function StatemanagerService(ngeoLocation, ngeoUsedKeyRegexp) { - - /** - * Object representing the application's initial state. - * @type {!Object.} - */ - this.initialState = {}; - - /** - * @type {!import("ngeo/statemanager/Location.js").StatemanagerLocation} - */ - this.ngeoLocation = ngeoLocation; - - +export class StatemanagerService { /** - * @type {!Array.} + * @param {import("ngeo/statemanager/Location.js").StatemanagerLocation} ngeoLocation ngeo location service. + * @param {Array} ngeoUsedKeyRegexp regexp used to identify the used keys. + * @ngInject */ - this.usedKeyRegexp = ngeoUsedKeyRegexp; - - /** - * @type {boolean} - * @private - */ - this.useLocalStorage_; - - this.setUseLocalStorage(false); - - // Populate initialState with the application's initial state. The initial - // state is read from the location URL, or from the local storage if there - // is no state in the location URL. - - const paramKeys = ngeoLocation.getParamKeys().filter(key => key != 'debug' && key != 'no_redirect'); - - if (paramKeys.length === 0) { - if (this.useLocalStorage_) { - for (const key in window.localStorage) { - console.assert(key); - + constructor(ngeoLocation, ngeoUsedKeyRegexp) { + + /** + * Object representing the application's initial state. + * @type {Object} + */ + this.initialState = {}; + + /** + * @type {import("ngeo/statemanager/Location.js").StatemanagerLocation} + */ + this.ngeoLocation = ngeoLocation; + + + /** + * @type {Array} + */ + this.usedKeyRegexp = ngeoUsedKeyRegexp; + + /** + * @type {boolean} + * @private + */ + this.useLocalStorage_ = false; + + this.setUseLocalStorage(false); + + // Populate initialState with the application's initial state. The initial + // state is read from the location URL, or from the local storage if there + // is no state in the location URL. + + const paramKeys = ngeoLocation.getParamKeys().filter(key => key != 'debug' && key != 'no_redirect'); + + if (paramKeys.length === 0) { + if (this.useLocalStorage_) { + for (const key in window.localStorage) { + console.assert(key); + + this.usedKeyRegexp.some((keyRegexp) => { + if (key.match(keyRegexp)) { + const value = window.localStorage[key]; + if (value !== undefined || value !== null) { + this.initialState[key] = value; + } else { + this.initialState[key] = ''; + } + return true; + } + return false; + }); + } + } + } else { + paramKeys.forEach((key) => { this.usedKeyRegexp.some((keyRegexp) => { if (key.match(keyRegexp)) { - const value = window.localStorage[key]; - if (value !== undefined || value !== null) { + const value = this.ngeoLocation.getParam(key); + if (value !== undefined) { this.initialState[key] = value; - } else { - this.initialState[key] = ''; + return true; } - return true; } + return false; }); - } - } - } else { - paramKeys.forEach((key) => { - this.usedKeyRegexp.some((keyRegexp) => { - if (key.match(keyRegexp)) { - const value = this.ngeoLocation.getParam(key); - if (value !== undefined) { - this.initialState[key] = value; - return true; - } - } }); - }); + } } -} -/** - * @param {boolean} value Use localStorage - * @return {boolean} localStorage will be used. - */ -StatemanagerService.prototype.setUseLocalStorage = function(value) { - this.useLocalStorage_ = value; - - // check if localStorage is supported - if (this.useLocalStorage_) { - try { - if ('localStorage' in window) { - window.localStorage['test'] = ''; - delete window.localStorage['test']; - } else { + /** + * @param {boolean} value Use localStorage + * @return {boolean} localStorage will be used. + */ + setUseLocalStorage(value) { + this.useLocalStorage_ = value; + + // check if localStorage is supported + if (this.useLocalStorage_) { + try { + if ('localStorage' in window) { + window.localStorage['test'] = ''; + delete window.localStorage['test']; + } else { + this.useLocalStorage_ = false; + } + } catch (err) { + console.error(err); this.useLocalStorage_ = false; } - } catch (err) { - console.error(err); - this.useLocalStorage_ = false; } + return this.useLocalStorage_; } - return this.useLocalStorage_; -}; - -/** - * Get the state value for `key`. - * @param {string} key State key. - * @return {string|undefined} State value. - */ -StatemanagerService.prototype.getInitialValue = function(key) { - return this.initialState[key]; -}; - -/** - * Get the state value for `key`. - * @param {string} key State key. - * @return {string|undefined} State value. - */ -StatemanagerService.prototype.getInitialStringValue = function(key) { - const value = this.initialState[key]; - if (value === undefined) { - return undefined; + /** + * Get the state value for `key`. + * @param {string} key State key. + * @return {string|undefined} State value. + */ + getInitialValue(key) { + return this.initialState[key]; } - return value; -}; - -/** - * Get the state value for `key`. - * @param {string} key State key. - * @return {number|undefined} State value. - */ -StatemanagerService.prototype.getInitialNumberValue = function(key) { - const value = this.initialState[key]; - if (value === undefined) { - return undefined; + /** + * Get the state value for `key`. + * @param {string} key State key. + * @return {string|undefined} State value. + */ + getInitialStringValue(key) { + const value = this.initialState[key]; + if (value === undefined) { + return undefined; + } + return value; } - return parseFloat(value); -}; - -/** - * Get the state value for `key`. - * @param {string} key State key. - * @return {boolean|undefined} State value. - */ -StatemanagerService.prototype.getInitialBooleanValue = function(key) { - const value = this.initialState[key]; - if (value === undefined) { - return undefined; + /** + * Get the state value for `key`. + * @param {string} key State key. + * @return {number|undefined} State value. + */ + getInitialNumberValue(key) { + const value = this.initialState[key]; + if (value === undefined) { + return undefined; + } + return parseFloat(value); } - return value === 'true'; -}; - -/** - * Update the application state with the values in `object`. - * @param {!Object.} object Object. - */ -StatemanagerService.prototype.updateState = function(object) { - this.ngeoLocation.updateParams(object); - if (this.useLocalStorage_) { - for (const key in object) { - console.assert(key); - const value = object[key]; - console.assert(value !== undefined); - window.localStorage[key] = value; + /** + * Get the state value for `key`. + * @param {string} key State key. + * @return {boolean|undefined} State value. + */ + getInitialBooleanValue(key) { + const value = this.initialState[key]; + if (value === undefined) { + return undefined; } + return value === 'true'; } -}; + /** + * Update the application state with the values in `object`. + * @param {!Object.} object Object. + */ + updateState(object) { + this.ngeoLocation.updateParams(object); + if (this.useLocalStorage_) { + for (const key in object) { + console.assert(key); + const value = object[key]; + console.assert(value !== undefined); + window.localStorage[key] = value; + } + } + } -/** - * Delete a parameter - * @param {string} key Key. - */ -StatemanagerService.prototype.deleteParam = function(key) { - this.ngeoLocation.deleteParam(key); - if (this.useLocalStorage_) { - delete window.localStorage[key]; + /** + * Delete a parameter + * @param {string} key Key. + */ + deleteParam(key) { + this.ngeoLocation.deleteParam(key); + if (this.useLocalStorage_) { + delete window.localStorage[key]; + } } -}; +} /** diff --git a/src/statemanager/WfsPermalink.js b/src/statemanager/WfsPermalink.js index 72f9a76da4c..6266f185b81 100644 --- a/src/statemanager/WfsPermalink.js +++ b/src/statemanager/WfsPermalink.js @@ -1,7 +1,8 @@ /* eslint max-len: ["error", { "code": 110, "ignoreComments": true }] */ import angular from 'angular'; -import * as olExtent from 'ol/extent.js'; +import {extend as extendExtent, createEmpty as createEmptyExtent} from 'ol/extent.js'; +import Feature from 'ol/Feature.js'; import {equalTo, and, or} from 'ol/format/filter.js'; import olFormatWFS from 'ol/format/WFS.js'; @@ -66,7 +67,7 @@ import olFormatWFS from 'ol/format/WFS.js'; /** * @typedef {Object} WfsPermalinkFilter * @property {string} property - * @property {Array.} condition + * @property {Array|string} condition */ /** @@ -247,7 +248,7 @@ WfsPermalinkService.prototype.issueRequest_ = function(wfsType, filter, map, sho // zoom to features const size = map.getSize(); - if (size !== undefined) { + if (size !== undefined && this.pointRecenterZoom_ !== undefined) { const maxZoom = this.pointRecenterZoom_; const padding = [10, 10, 10, 10]; map.getView().fit(this.getExtent_(features), {size, maxZoom, padding}); @@ -271,46 +272,63 @@ WfsPermalinkService.prototype.issueRequest_ = function(wfsType, filter, map, sho /** - * @param {Array.} features Features. - * @return {import("ol/extent.js").Extent} The extent of all features. + * @param {!Array} features Features. + * @return {!import('ol/extent.js').Extent} The extent of all features. * @private */ WfsPermalinkService.prototype.getExtent_ = function(features) { - return features.reduce( - (extent, feature) => olExtent.extend(extent, feature.getGeometry().getExtent()), olExtent.createEmpty() - ); + return /** @type{import('ol/extent.js').Extent} */(/** @type{Array} */(features).reduce( + (extent, feature) => { + if (feature instanceof Feature) { + const geometry = feature.getGeometry(); + if (geometry) { + return extendExtent(extent, geometry.getExtent()); + } + return extent; + } + }, createEmptyExtent() + )); }; /** * Create OGC filters for the filter groups extracted from the query params. * - * @param {Array.} filterGroups Filter groups. - * @return {import("ol/format/filter/Filter.js").default} OGC filters. + * @param {Array} filterGroups Filter groups. + * @return {?import("ol/format/filter/Filter.js").default} OGC filters. * @private */ WfsPermalinkService.prototype.createFilters_ = function(filterGroups) { if (filterGroups.length == 0) { return null; } + /** + * The function + * @param {WfsPermalinkFilterGroup} filterGroup The filter + * @returns {import("ol/format/filter/Filter.js").default} The return + */ const createFiltersForGroup = function(filterGroup) { const filters = filterGroup.filters.map((filterDef) => { const condition = filterDef.condition; if (Array.isArray(condition)) { return WfsPermalinkService.or_(condition.map(cond => equalTo(filterDef.property, cond))); } else { - return equalTo(filterDef.property, filterDef.condition); + return equalTo(filterDef.property, condition); } }); return WfsPermalinkService.and_(filters); }; - return WfsPermalinkService.or_(filterGroups.map(createFiltersForGroup)); + const filters = filterGroups.map(createFiltersForGroup); + if (filters) { + return WfsPermalinkService.or_(filters); + } + return null; }; /** * Join a list of filters with `and(...)`. * - * @param {Array.} filters The filters to join. + * @param {Array} filters The filters to join. * @return {import("ol/format/filter/Filter.js").default} The joined filters. * @private */ @@ -322,7 +340,7 @@ WfsPermalinkService.and_ = function(filters) { /** * Join a list of filters with `or(...)`. * - * @param {Array.} filters The filters to join. + * @param {Array} filters The filters to join. * @return {import("ol/format/filter/Filter.js").default} The joined filters. * @private */ @@ -334,7 +352,7 @@ WfsPermalinkService.or_ = function(filters) { /** * Join a list of filters with a given join function. * - * @param {Array.} filters The filters to join. + * @param {Array} filters The filters to join. * @param {function(!import("ol/format/filter/Filter.js").default, !import("ol/format/filter/Filter.js").default): import("ol/format/filter/Filter.js").default} joinFn * The function to join two filters. * @return {import("ol/format/filter/Filter.js").default} The joined filters. @@ -348,7 +366,7 @@ WfsPermalinkService.joinFilters_ = function(filters, joinFn) { console.assert(currentFilter !== null); return joinFn(combinedFilters, currentFilter); } - }, null); + }); }; diff --git a/test/spec/services/print.spec.js b/test/spec/services/print.spec.js index eb347d0807c..c5501400c1f 100644 --- a/test/spec/services/print.spec.js +++ b/test/spec/services/print.spec.js @@ -83,15 +83,17 @@ describe('ngeo.print.Service', () => { describe('ImageWMS', () => { beforeEach(() => { + const source = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: 'http://example.com/wms', + params: { + 'LAYERS': 'foo,bar', + 'FORMAT': 'image/jpeg' + } + }); + // @ts-ignore: OL issue map.addLayer(new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: 'http://example.com/wms', - params: { - 'LAYERS': 'foo,bar', - 'FORMAT': 'image/jpeg' - } - }) + source })); }); @@ -405,6 +407,7 @@ describe('ngeo.print.Service', () => { } else if (v == '3') { return styles3; } + return []; }; map.addLayer(new olLayerVector({ @@ -595,28 +598,31 @@ describe('ngeo.print.Service', () => { describe('layer order', () => { beforeEach(() => { + const source1 = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: 'http://example.com/wms/bottom', + params: { + 'LAYERS': 'foo,bar', + 'FORMAT': 'image/jpeg' + } + }); + // @ts-ignore: OL issue map.addLayer(new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: 'http://example.com/wms/bottom', - params: { - 'LAYERS': 'foo,bar', - 'FORMAT': 'image/jpeg' - } - }) + source: source1 })); + const source2 = new olSourceImageWMS({ + projection: undefined, // should be removed in next OL version + url: 'http://example.com/wms/top', + params: { + 'LAYERS': 'foo,bar', + 'FORMAT': 'image/jpeg' + } + }); + // @ts-ignore: OL issue map.addLayer(new olLayerImage({ - source: new olSourceImageWMS({ - projection: undefined, // should be removed in next OL version - url: 'http://example.com/wms/top', - params: { - 'LAYERS': 'foo,bar', - 'FORMAT': 'image/jpeg' - } - }) + source: source2 })); - }); it('reverses the layer order', () => { diff --git a/tsconfig.json b/tsconfig.json index c94ddb728f6..3c357120883 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "noImplicitThis": true, "alwaysStrict": true, "strictBindCallApply": true, - //"strictNullChecks": true, + "strictNullChecks": true, "strictFunctionTypes": true, //"strictPropertyInitialization": true, // End strict