From 01846fafc67da50ece21ecf9326d2c2dc6959dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Thu, 18 Jul 2019 11:32:23 +0200 Subject: [PATCH 01/10] Fix triangle in object editing --- src/editing/createregularpolygonfromclickComponent.js | 2 +- src/interaction/DrawRegularPolygonFromClick.js | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/editing/createregularpolygonfromclickComponent.js b/src/editing/createregularpolygonfromclickComponent.js index 0964fe8d9d31..208a655adf14 100644 --- a/src/editing/createregularpolygonfromclickComponent.js +++ b/src/editing/createregularpolygonfromclickComponent.js @@ -180,7 +180,7 @@ exports.Controller_.prototype.$onInit = function() { * @private */ exports.Controller_.prototype.handleDrawEnd_ = function(evt) { - const feature = new olFeature(evt.feature.getGeometry()); + const feature = new olFeature(evt.detail.feature.getGeometry()); this.features.push(feature); }; diff --git a/src/interaction/DrawRegularPolygonFromClick.js b/src/interaction/DrawRegularPolygonFromClick.js index 82e69e3cf210..a64079943e96 100644 --- a/src/interaction/DrawRegularPolygonFromClick.js +++ b/src/interaction/DrawRegularPolygonFromClick.js @@ -8,7 +8,7 @@ import * as olEvents from 'ol/events.js'; import olFeature from 'ol/Feature.js'; import * as olFunctions from 'ol/functions.js'; import olGeomCircle from 'ol/geom/Circle.js'; -import olGeomPolygon from 'ol/geom/Polygon.js'; +import {fromCircle, makeRegular} from 'ol/geom/Polygon.js'; import olInteractionInteraction from 'ol/interaction/Interaction.js'; /** @@ -130,11 +130,12 @@ exports.prototype.disable_ = function() { */ exports.prototype.handleMapClick_ = function(evt) { const center = evt.coordinate; - const geometry = olGeomPolygon.fromCircle( + const geometry = fromCircle( new olGeomCircle(center), this.sides_ ); - olGeomPolygon.makeRegular(geometry, center, this.radius_, this.angle_); + makeRegular(geometry, center, this.radius_, this.angle_); + /** @type {ngeox.DrawEvent} */ const event = new ngeoCustomEvent('drawend', {feature: new olFeature(geometry)}); From f930fc0cd60ae3cc95bb287490bdfa83ca61bfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Thu, 18 Jul 2019 13:17:52 +0200 Subject: [PATCH 02/10] Fix object editing interface on ie --- buildtools/webpack.commons.js | 14 ++++++++++++++ contribs/gmf/src/objectediting/toolsComponent.js | 3 +-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/buildtools/webpack.commons.js b/buildtools/webpack.commons.js index e5a376bb5b8b..81d9fc1ab5a7 100644 --- a/buildtools/webpack.commons.js +++ b/buildtools/webpack.commons.js @@ -158,6 +158,19 @@ const config = function(hardSourceConfig, babelLoaderCacheDirectory) { } }; + const jstsRule = { + test: /jsts\/.*\.js$/, + use: { + loader: 'babel-loader', + options: { + babelrc: false, + comments: false, + cacheDirectory: babelLoaderCacheDirectory, + presets: babelPresets, + } + } + }; + return { context: path.resolve(__dirname, '../'), devtool: 'source-map', @@ -168,6 +181,7 @@ const config = function(hardSourceConfig, babelLoaderCacheDirectory) { rules: [ olRule, olcsRule, + jstsRule, angularRule, typeaheadRule, cssRule, diff --git a/contribs/gmf/src/objectediting/toolsComponent.js b/contribs/gmf/src/objectediting/toolsComponent.js index 8747aa86386d..eb9cc6a6438c 100644 --- a/contribs/gmf/src/objectediting/toolsComponent.js +++ b/contribs/gmf/src/objectediting/toolsComponent.js @@ -362,8 +362,7 @@ exports.Controller_.prototype.handleToolActiveChange_ = function( exports.Controller_.prototype.handleDestroy_ = function() {}; -exports.controller('GmfObjecteditingtoolsController', - exports.Controller_); +exports.controller('GmfObjecteditingtoolsController', exports.Controller_); /** From 3eb0d06482f5f0bdd50670ff81195dbe533b4256 Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Tue, 18 Jun 2019 15:46:11 +0200 Subject: [PATCH 03/10] Add orthogonality to linestring drawing --- .../controllers/AbstractDesktopController.js | 1 - contribs/gmf/src/editing/Snapping.js | 59 ++++++++- examples/drawfeature.js | 1 + src/editing/createfeatureComponent.js | 13 +- src/interaction/MeasureLength.js | 117 +++++++++++++++++- src/measure/length.js | 3 +- 6 files changed, 185 insertions(+), 9 deletions(-) diff --git a/contribs/gmf/src/controllers/AbstractDesktopController.js b/contribs/gmf/src/controllers/AbstractDesktopController.js index 57a2c4e2ed5b..5e0e7ee3e423 100644 --- a/contribs/gmf/src/controllers/AbstractDesktopController.js +++ b/contribs/gmf/src/controllers/AbstractDesktopController.js @@ -254,5 +254,4 @@ exports.module.value('ngeoQueryOptions', { exports.module.value('ngeoMeasurePrecision', 3); exports.module.value('ngeoMeasureDecimals', 0); - export default exports; diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 3aeccfcde82b..09f8824326cd 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -26,6 +26,7 @@ import olInteractionSnap from 'ol/interaction/Snap.js'; * @param {angular.$http} $http Angular $http service. * @param {angular.$q} $q The Angular $q service. * @param {!angular.Scope} $rootScope Angular rootScope. + * @param {!angular.$injector} $injector Angular injector. * @param {angular.$timeout} $timeout Angular timeout service. * @param {gmf.theme.Themes} gmfThemes The gmf Themes service. * @param {gmf.layertree.TreeManager} gmfTreeManager The gmf TreeManager service. @@ -33,7 +34,7 @@ import olInteractionSnap from 'ol/interaction/Snap.js'; * @ngdoc service * @ngname gmfSnapping */ -const exports = function($http, $q, $rootScope, $timeout, gmfThemes, +const exports = function($http, $q, $rootScope, $injector, $timeout, gmfThemes, gmfTreeManager) { // === Injected services === @@ -62,6 +63,12 @@ const exports = function($http, $q, $rootScope, $timeout, gmfThemes, */ this.timeout_ = $timeout; + /** + * @type {!angular.$injector} + * @private + */ + this.injector_ = $injector; + /** * @type {gmf.theme.Themes} * @private @@ -113,13 +120,46 @@ const exports = function($http, $q, $rootScope, $timeout, gmfThemes, */ this.ogcServers_ = null; + /** + * @type {ol.source.Vector | undefined} + * @private + */ + this.ngeoSnappingSource_ = this.injector_.get('ngeoSnappingSource') || undefined; + }; +class CustomSnap extends olInteractionSnap { + constructor(options) { + super(options); + this.modifierPressed = false; + document.body.addEventListener('keydown', (evt) => { + this.modifierPressed = evt.keyCode === 17; // Ctrl key + }); + document.body.addEventListener('keyup', () => { + this.modifierPressed = false; + }); + } + + handleEvent(evt) { + if (this.modifierPressed) { + return; + } else { + const result = this.snapTo(evt.pixel, evt.coordinate, evt.map); + if (result.snapped) { + evt.coordinate = result.vertex.slice(0, 2); + evt.pixel = result.vertexPixel; + } + return super.handleEvent(evt); + } + } +} + + /** * In order for a `ol.interaction.Snap` to work properly, it has to be added * to the map after any draw interactions or other kinds of interactions that - * ineracts with features on the map. + * interacts with features on the map. * * This method can be called to make sure the Snap interactions are on top. * @@ -384,7 +424,7 @@ exports.prototype.activateItem_ = function(item) { const map = this.map_; googAsserts.assert(map); - const interaction = new olInteractionSnap({ + const interaction = new CustomSnap({ edge: item.snappingConfig.edge, features: item.features, pixelTolerance: item.snappingConfig.tolerance, @@ -514,6 +554,7 @@ exports.prototype.loadItemFeatures_ = function(item) { const readFeatures = new olFormatWFS().readFeatures(response.data); if (readFeatures) { item.features.extend(readFeatures); + this.refreshSnappingSource_(); } }); @@ -535,6 +576,18 @@ exports.prototype.handleMapMoveEnd_ = function() { ); }; +/** + * @private + */ +exports.prototype.refreshSnappingSource_ = function() { + this.ngeoSnappingSource_.clear(); + for (const uid in this.cache_) { + const item = this.cache_[+uid]; + if (item.active) { + this.ngeoSnappingSource_.addFeatures(item.features.getArray()); + } + } +}; /** * @typedef {Object} diff --git a/examples/drawfeature.js b/examples/drawfeature.js index ef86becf03c8..ec97894bb56d 100644 --- a/examples/drawfeature.js +++ b/examples/drawfeature.js @@ -25,6 +25,7 @@ exports.module = angular.module('app', [ ngeoMiscToolActivateMgr.module.name, ]); +exports.module.value('ngeoMeasureTolerance', 20); /** * @param {!angular.Scope} $scope Angular scope. diff --git a/src/editing/createfeatureComponent.js b/src/editing/createfeatureComponent.js index 9d2b484b4143..f46a9db2c3d3 100644 --- a/src/editing/createfeatureComponent.js +++ b/src/editing/createfeatureComponent.js @@ -79,6 +79,7 @@ exports.directive('ngeoCreatefeature', exports.directive_); * @param {!angularGettext.Catalog} gettextCatalog Gettext catalog. * @param {!angular.$compile} $compile Angular compile service. * @param {!angular.$filter} $filter Angular filter + * @param {!angular.$injector} $injector Angular injector service. * @param {!angular.Scope} $scope Scope. * @param {!angular.$timeout} $timeout Angular timeout service. * @param {!ngeo.misc.EventHelper} ngeoEventHelper Ngeo event helper service @@ -89,7 +90,7 @@ exports.directive('ngeoCreatefeature', exports.directive_); * @ngdoc controller * @ngname ngeoCreatefeatureController */ -exports.Controller_ = function(gettextCatalog, $compile, $filter, $scope, +exports.Controller_ = function(gettextCatalog, $compile, $filter, $injector, $scope, $timeout, ngeoEventHelper) { /** @@ -152,6 +153,12 @@ exports.Controller_ = function(gettextCatalog, $compile, $filter, $scope, */ this.ngeoEventHelper_ = ngeoEventHelper; + /** + * @type {!angular.$injector} + * @private + */ + this.injector_ = $injector; + /** * The draw or measure interaction responsible of drawing the vector feature. * The actual type depends on the geometry type. @@ -201,7 +208,9 @@ exports.Controller_.prototype.$onInit = function() { { style: new olStyleStyle(), startMsg: this.compile_(`
${helpMsg}
`)(this.scope_)[0], - continueMsg: this.compile_(`
${contMsg}
`)(this.scope_)[0] + continueMsg: this.compile_(`
${contMsg}
`)(this.scope_)[0], + tolerance: this.injector_.has('ngeoMeasureTolerance') ? this.injector_.get('ngeoMeasureTolerance') : undefined, + source: this.injector_.has('ngeoSnappingSource') ? this.injector_.get('ngeoSnappingSource') : undefined, } ); } else if (this.geomType === ngeoGeometryType.POLYGON || diff --git a/src/interaction/MeasureLength.js b/src/interaction/MeasureLength.js index a1763e12f60f..aebbc21d4d41 100644 --- a/src/interaction/MeasureLength.js +++ b/src/interaction/MeasureLength.js @@ -6,6 +6,10 @@ import ngeoInteractionMeasure from 'ngeo/interaction/Measure.js'; import * as olBase from 'ol/index.js'; import olGeomLineString from 'ol/geom/LineString.js'; import olInteractionDraw from 'ol/interaction/Draw.js'; +import {distance} from 'ol/coordinate.js'; + +let modifierPressed = undefined; + /** * @classdesc @@ -24,6 +28,15 @@ const exports = function(format, gettextCatalog, options = /** @type {ngeox.inte ngeoInteractionMeasure.call(this, /** @type {ngeo.interaction.MeasureBaseOptions} */ (options)); + if (modifierPressed === undefined) { + modifierPressed = false; + document.body.addEventListener('keydown', (evt) => { + modifierPressed = evt.keyCode === 17; // Ctrl key + }); + document.body.addEventListener('keyup', () => { + modifierPressed = false; + }); + } if (options.continueMsg !== undefined) { this.continueMsg = options.continueMsg; @@ -41,6 +54,13 @@ const exports = function(format, gettextCatalog, options = /** @type {ngeox.inte */ this.format = format; + /** + * The tolerance for snapping in pixels. + * @params {Number} + */ + this.tolerance = options.tolerance; + + this.source = options.source; }; olBase.inherits(exports, ngeoInteractionMeasure); @@ -49,15 +69,74 @@ olBase.inherits(exports, ngeoInteractionMeasure); /** * @inheritDoc */ -exports.prototype.createDrawInteraction = function(style, source) { +exports.prototype.createDrawInteraction = function(style) { return new olInteractionDraw({ type: /** @type {ol.geom.GeometryType} */ ('LineString'), - source: source, + geometryFunction: (...args) => + this.linestringGeometryFunction(...args), + condition: () => true, style: style }); }; +/** + * Create a `linestringGeometryFunction` that will create a line string with segments + * snapped to π/4 angle. + * Use this with the draw interaction and `type: 'LineString'`. + * @param {LineCoordType} coordinates Coordinates. + * @param {LineString=} opt_geometry Geometry. + * @return {LineString} Geometry. + */ +exports.prototype.linestringGeometryFunction = function(coordinates, opt_geometry) { + if (modifierPressed && this.tolerance !== undefined && this.source !== undefined) { + const viewRotation = this.getMap().getView().getRotation(); + const angle = Math.PI / 4; + const from = coordinates[coordinates.length - 2]; + const to = coordinates[coordinates.length - 1]; + const dx = from[0] - to[0]; + const dy = from[1] - to[1]; + const length = distance(from, to); + const rotation = viewRotation + Math.round((Math.atan2(dy, dx) - viewRotation) / angle) * angle; + + to[0] = from[0] - (length * Math.cos(rotation)); + to[1] = from[1] - (length * Math.sin(rotation)); + + const delta = this.getMap().getView().getResolution() * this.tolerance; + const bbox = [to[0] - delta, to[1] - delta, to[0] + delta, to[1] + delta]; + + const layerSource = this.source; + const featuresInExtent = layerSource.getFeaturesInExtent(bbox); + if (featuresInExtent.length > 0) { + featuresInExtent.forEach((feature) => { + feature.getGeometry().forEachSegment((point1, point2) => { + // Line points are: from A to M (to B that we need to find) + const distanceFromTo = distance(from, to); + const ax = from[0]; + const ay = from[1]; + const mx = to[0]; + const my = to[1]; + const unitVector = [(mx - ax) / distanceFromTo, (my - ay) / distanceFromTo]; + const b = [(ax + (distanceFromTo + delta) * unitVector[0]), (ay + (distanceFromTo + delta) * unitVector[1])]; + to[0] = b[0]; + to[1] = b[1]; + + // intersection calculation + this.computeLineSegmentIntersection(from, to, point1, point2, to); + }); + }); + } + } + + const geometry = opt_geometry; + if (geometry) { + geometry.setCoordinates(coordinates); + return geometry; + } + return new olGeomLineString(coordinates); +}; + + /** * @inheritDoc */ @@ -70,5 +149,39 @@ exports.prototype.handleMeasure = function(callback) { callback(output, coord); }; +/** + * Compute the intersection between 2 segments + * + * @param {Number} line1vertex1 The coordinates of the first line's first vertex. + * @param {Number} line1vertex2 The coordinates of the first line's second vertex. + * @param {Number} line2vertex1 The coordinates of the second line's first vertex. + * @param {Number} line2vertex2 The coordinates of the second line's second vertex. + * @param {Array} [result] An existing array where the result will be copied. If this parameter + * is undefined, a new array is created and returned. + * @returns {Array | undefined} The intersection point, undefined if there is no intersection point or lines are coincident. + */ +exports.prototype.computeLineSegmentIntersection = function(line1vertex1, line1vertex2, line2vertex1, line2vertex2, result) { + const numerator1A = (line2vertex2[0] - line2vertex1[0]) * (line1vertex1[1] - line2vertex1[1]) + - (line2vertex2[1] - line2vertex1[1]) * (line1vertex1[0] - line2vertex1[0]); + const numerator1B = (line1vertex2[0] - line1vertex1[0]) * (line1vertex1[1] - line2vertex1[1]) + - (line1vertex2[1] - line1vertex1[1]) * (line1vertex1[0] - line2vertex1[0]); + const denominator1 = (line2vertex2[1] - line2vertex1[1]) * (line1vertex2[0] - line1vertex1[0]) + - (line2vertex2[0] - line2vertex1[0]) * (line1vertex2[1] - line1vertex1[1]); + + // If denominator = 0, then lines are parallel. If denominator = 0 and both numerators are 0, then coincident + if (denominator1 === 0) { + return; + } + + const ua1 = numerator1A / denominator1; + const ub1 = numerator1B / denominator1; + + if (ua1 >= 0 && ua1 <= 1 && ub1 >= 0 && ub1 <= 1) { + result[0] = line1vertex1[0] + ua1 * (line1vertex2[0] - line1vertex1[0]); + result[1] = line1vertex1[1] + ua1 * (line1vertex2[1] - line1vertex1[1]); + return result; + } +}; + export default exports; diff --git a/src/measure/length.js b/src/measure/length.js index fff95c946c80..12b6dd9c733f 100644 --- a/src/measure/length.js +++ b/src/measure/length.js @@ -47,7 +47,8 @@ exports.directive_ = function($compile, gettextCatalog, $filter, $injector) { style: new olStyleStyle(), startMsg: $compile(`
${helpMsg}
`)($scope)[0], continueMsg: $compile(`
${contMsg}
`)($scope)[0], - precision: $injector.has('ngeoMeasurePrecision') ? $injector.get('ngeoMeasurePrecision') : undefined + precision: $injector.has('ngeoMeasurePrecision') ? $injector.get('ngeoMeasurePrecision') : undefined, + tolerance: $injector.has('ngeoMeasureTolerance') ? $injector.get('ngeoMeasureTolerance') : undefined, }); drawFeatureCtrl.registerInteraction(measureLength); From 9d4d193832ad1af4ef2afa3aecb1b9ec7c40657e Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Mon, 22 Jul 2019 16:17:44 +0200 Subject: [PATCH 04/10] Rename tolerance value --- examples/drawfeature.js | 2 -- src/editing/createfeatureComponent.js | 2 +- src/interaction/MeasureLength.js | 6 +++++- src/measure/length.js | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/drawfeature.js b/examples/drawfeature.js index ec97894bb56d..901a1458d1ee 100644 --- a/examples/drawfeature.js +++ b/examples/drawfeature.js @@ -25,8 +25,6 @@ exports.module = angular.module('app', [ ngeoMiscToolActivateMgr.module.name, ]); -exports.module.value('ngeoMeasureTolerance', 20); - /** * @param {!angular.Scope} $scope Angular scope. * @param {ol.Collection.} ngeoFeatures Collection of features. diff --git a/src/editing/createfeatureComponent.js b/src/editing/createfeatureComponent.js index f46a9db2c3d3..9d5cc5a2b671 100644 --- a/src/editing/createfeatureComponent.js +++ b/src/editing/createfeatureComponent.js @@ -209,7 +209,7 @@ exports.Controller_.prototype.$onInit = function() { style: new olStyleStyle(), startMsg: this.compile_(`
${helpMsg}
`)(this.scope_)[0], continueMsg: this.compile_(`
${contMsg}
`)(this.scope_)[0], - tolerance: this.injector_.has('ngeoMeasureTolerance') ? this.injector_.get('ngeoMeasureTolerance') : undefined, + tolerance: this.injector_.has('ngeoSnappingTolerance') ? this.injector_.get('ngeoSnappingTolerance') : undefined, source: this.injector_.has('ngeoSnappingSource') ? this.injector_.get('ngeoSnappingSource') : undefined, } ); diff --git a/src/interaction/MeasureLength.js b/src/interaction/MeasureLength.js index aebbc21d4d41..20747da7e94f 100644 --- a/src/interaction/MeasureLength.js +++ b/src/interaction/MeasureLength.js @@ -55,11 +55,15 @@ const exports = function(format, gettextCatalog, options = /** @type {ngeox.inte this.format = format; /** - * The tolerance for snapping in pixels. + * The snapping tolerance in pixels. * @params {Number} */ this.tolerance = options.tolerance; + /** + * The snapping source + * @params {ol.source.Vector} + */ this.source = options.source; }; diff --git a/src/measure/length.js b/src/measure/length.js index 12b6dd9c733f..22f8d2c0302a 100644 --- a/src/measure/length.js +++ b/src/measure/length.js @@ -48,7 +48,8 @@ exports.directive_ = function($compile, gettextCatalog, $filter, $injector) { startMsg: $compile(`
${helpMsg}
`)($scope)[0], continueMsg: $compile(`
${contMsg}
`)($scope)[0], precision: $injector.has('ngeoMeasurePrecision') ? $injector.get('ngeoMeasurePrecision') : undefined, - tolerance: $injector.has('ngeoMeasureTolerance') ? $injector.get('ngeoMeasureTolerance') : undefined, + tolerance: $injector.has('ngeoSnappingTolerance') ? $injector.get('ngeoSnappingTolerance') : undefined, + source: $injector.has('ngeoSnappingSource') ? $injector.get('ngeoSnappingSource') : undefined, }); drawFeatureCtrl.registerInteraction(measureLength); From 8bbb7d31ba9db2c0e277f0141e1a5d8c8bd0b5a5 Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Tue, 23 Jul 2019 15:29:15 +0200 Subject: [PATCH 05/10] Clean GeometryFunction --- contribs/gmf/src/editing/Snapping.js | 10 +++++----- src/interaction/MeasureLength.js | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 09f8824326cd..5b29daf1d8a1 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -171,7 +171,7 @@ exports.prototype.ensureSnapInteractionsOnTop = function() { let item; for (const uid in this.cache_) { - item = this.cache_[+uid]; + item = this.cache_[uid]; if (item.active) { googAsserts.assert(item.interaction); map.removeInteraction(item.interaction); @@ -298,11 +298,11 @@ exports.prototype.registerTreeCtrl_ = function(treeCtrl) { */ exports.prototype.unregisterAllTreeCtrl_ = function() { for (const uid in this.cache_) { - const item = this.cache_[+uid]; + const item = this.cache_[uid]; if (item) { item.stateWatcherUnregister(); this.deactivateItem_(item); - delete this.cache_[+uid]; + delete this.cache_[uid]; } } }; @@ -481,7 +481,7 @@ exports.prototype.loadAllItems_ = function() { this.mapViewChangePromise_ = null; let item; for (const uid in this.cache_) { - item = this.cache_[+uid]; + item = this.cache_[uid]; if (item.active) { this.loadItemFeatures_(item); } @@ -582,7 +582,7 @@ exports.prototype.handleMapMoveEnd_ = function() { exports.prototype.refreshSnappingSource_ = function() { this.ngeoSnappingSource_.clear(); for (const uid in this.cache_) { - const item = this.cache_[+uid]; + const item = this.cache_[uid]; if (item.active) { this.ngeoSnappingSource_.addFeatures(item.features.getArray()); } diff --git a/src/interaction/MeasureLength.js b/src/interaction/MeasureLength.js index 20747da7e94f..952e28641e4a 100644 --- a/src/interaction/MeasureLength.js +++ b/src/interaction/MeasureLength.js @@ -56,7 +56,7 @@ const exports = function(format, gettextCatalog, options = /** @type {ngeox.inte /** * The snapping tolerance in pixels. - * @params {Number} + * @params {number} */ this.tolerance = options.tolerance; @@ -73,13 +73,13 @@ olBase.inherits(exports, ngeoInteractionMeasure); /** * @inheritDoc */ -exports.prototype.createDrawInteraction = function(style) { +exports.prototype.createDrawInteraction = function(style, source) { return new olInteractionDraw({ type: /** @type {ol.geom.GeometryType} */ ('LineString'), - geometryFunction: (...args) => - this.linestringGeometryFunction(...args), + geometryFunction: this.linestringGeometryFunction.bind(this), condition: () => true, - style: style + style: style, + source: source, }); }; @@ -93,7 +93,7 @@ exports.prototype.createDrawInteraction = function(style) { * @return {LineString} Geometry. */ exports.prototype.linestringGeometryFunction = function(coordinates, opt_geometry) { - if (modifierPressed && this.tolerance !== undefined && this.source !== undefined) { + if (modifierPressed) { const viewRotation = this.getMap().getView().getRotation(); const angle = Math.PI / 4; const from = coordinates[coordinates.length - 2]; @@ -106,12 +106,12 @@ exports.prototype.linestringGeometryFunction = function(coordinates, opt_geometr to[0] = from[0] - (length * Math.cos(rotation)); to[1] = from[1] - (length * Math.sin(rotation)); - const delta = this.getMap().getView().getResolution() * this.tolerance; - const bbox = [to[0] - delta, to[1] - delta, to[0] + delta, to[1] + delta]; + if (this.tolerance !== undefined && this.source !== undefined) { + const delta = this.getMap().getView().getResolution() * this.tolerance; + const bbox = [to[0] - delta, to[1] - delta, to[0] + delta, to[1] + delta]; - const layerSource = this.source; - const featuresInExtent = layerSource.getFeaturesInExtent(bbox); - if (featuresInExtent.length > 0) { + const layerSource = this.source; + const featuresInExtent = layerSource.getFeaturesInExtent(bbox); featuresInExtent.forEach((feature) => { feature.getGeometry().forEachSegment((point1, point2) => { // Line points are: from A to M (to B that we need to find) From 851613e5fd4dd54298897978afbb7c38e9166133 Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Wed, 24 Jul 2019 15:49:08 +0200 Subject: [PATCH 06/10] Fix intersection calculation --- contribs/gmf/src/editing/Snapping.js | 1 + src/interaction/MeasureLength.js | 47 ++++++++++++++++++---------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 5b29daf1d8a1..41be9667a18e 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -471,6 +471,7 @@ exports.prototype.deactivateItem_ = function(item) { } item.active = false; + this.refreshSnappingSource_(); }; diff --git a/src/interaction/MeasureLength.js b/src/interaction/MeasureLength.js index 952e28641e4a..2609501c94fa 100644 --- a/src/interaction/MeasureLength.js +++ b/src/interaction/MeasureLength.js @@ -7,6 +7,7 @@ import * as olBase from 'ol/index.js'; import olGeomLineString from 'ol/geom/LineString.js'; import olInteractionDraw from 'ol/interaction/Draw.js'; import {distance} from 'ol/coordinate.js'; +import {containsXY} from 'ol/extent'; let modifierPressed = undefined; @@ -113,21 +114,36 @@ exports.prototype.linestringGeometryFunction = function(coordinates, opt_geometr const layerSource = this.source; const featuresInExtent = layerSource.getFeaturesInExtent(bbox); featuresInExtent.forEach((feature) => { - feature.getGeometry().forEachSegment((point1, point2) => { - // Line points are: from A to M (to B that we need to find) - const distanceFromTo = distance(from, to); - const ax = from[0]; - const ay = from[1]; - const mx = to[0]; - const my = to[1]; - const unitVector = [(mx - ax) / distanceFromTo, (my - ay) / distanceFromTo]; - const b = [(ax + (distanceFromTo + delta) * unitVector[0]), (ay + (distanceFromTo + delta) * unitVector[1])]; - to[0] = b[0]; - to[1] = b[1]; + let lastIntersection = []; + let bestIntersection = []; + let bestDistance = Infinity; + + // Line points are: from A to M (to B that we need to find) + const distanceFromTo = distance(from, to); + const ax = from[0]; + const ay = from[1]; + const mx = to[0]; + const my = to[1]; + const unitVector = [(mx - ax) / distanceFromTo, (my - ay) / distanceFromTo]; + const b = [(ax + (distanceFromTo + delta) * unitVector[0]), (ay + (distanceFromTo + delta) * unitVector[1])]; + + feature.getGeometry().forEachSegment((point1, point2) => { // intersection calculation - this.computeLineSegmentIntersection(from, to, point1, point2, to); + lastIntersection = this.computeLineSegmentIntersection(from, b, point1, point2); + if (lastIntersection !== undefined && containsXY(bbox, lastIntersection[0], lastIntersection[1])) { + const lastDistance = distance(to, lastIntersection); + if (lastDistance < bestDistance) { + bestDistance = lastDistance; + bestIntersection = lastIntersection; + } + } }); + + if (bestIntersection) { + to[0] = bestIntersection[0] || to[0]; + to[1] = bestIntersection[1] || to[1]; + } }); } } @@ -160,11 +176,9 @@ exports.prototype.handleMeasure = function(callback) { * @param {Number} line1vertex2 The coordinates of the first line's second vertex. * @param {Number} line2vertex1 The coordinates of the second line's first vertex. * @param {Number} line2vertex2 The coordinates of the second line's second vertex. - * @param {Array} [result] An existing array where the result will be copied. If this parameter - * is undefined, a new array is created and returned. - * @returns {Array | undefined} The intersection point, undefined if there is no intersection point or lines are coincident. + * @return {Array | undefined} The intersection point, undefined if there is no intersection point or lines are coincident. */ -exports.prototype.computeLineSegmentIntersection = function(line1vertex1, line1vertex2, line2vertex1, line2vertex2, result) { +exports.prototype.computeLineSegmentIntersection = function(line1vertex1, line1vertex2, line2vertex1, line2vertex2) { const numerator1A = (line2vertex2[0] - line2vertex1[0]) * (line1vertex1[1] - line2vertex1[1]) - (line2vertex2[1] - line2vertex1[1]) * (line1vertex1[0] - line2vertex1[0]); const numerator1B = (line1vertex2[0] - line1vertex1[0]) * (line1vertex1[1] - line2vertex1[1]) @@ -181,6 +195,7 @@ exports.prototype.computeLineSegmentIntersection = function(line1vertex1, line1v const ub1 = numerator1B / denominator1; if (ua1 >= 0 && ua1 <= 1 && ub1 >= 0 && ub1 <= 1) { + const result = []; result[0] = line1vertex1[0] + ua1 * (line1vertex2[0] - line1vertex1[0]); result[1] = line1vertex1[1] + ua1 * (line1vertex2[1] - line1vertex1[1]); return result; From eabe6c7fc694b9b928ebd1c65ce7e4bc71b71e3e Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Thu, 25 Jul 2019 09:21:28 +0200 Subject: [PATCH 07/10] Add example in desktop interface and fix issue with snapping --- contribs/gmf/apps/desktop/Controller.js | 4 ++++ contribs/gmf/src/editing/Snapping.js | 21 ++++++++------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/contribs/gmf/apps/desktop/Controller.js b/contribs/gmf/apps/desktop/Controller.js index 70e31cc0f0c2..cef2a8bf4b9b 100644 --- a/contribs/gmf/apps/desktop/Controller.js +++ b/contribs/gmf/apps/desktop/Controller.js @@ -16,6 +16,7 @@ import ngeoProjEPSG21781 from 'ngeo/proj/EPSG21781.js'; import * as olBase from 'ol/index.js'; import Raven from 'raven-js/src/raven.js'; import RavenPluginsAngular from 'raven-js/plugins/angular.js'; +import olSourceVector from 'ol/source/Vector.js'; if (!window.requestAnimationFrame) { alert('Your browser is not supported, please update it or use another one. You will be redirected.\n\n' @@ -123,6 +124,9 @@ exports.module = angular.module('Appdesktop', [ gmfControllersAbstractDesktopController.module.name, ]); +exports.module.value('ngeoSnappingTolerance', 20); +exports.module.value('ngeoSnappingSource', new olSourceVector()); + exports.module.value('gmfContextualdatacontentTemplateUrl', 'gmf/contextualdata'); exports.module.run(/* @ngInject */ ($templateCache) => { $templateCache.put('gmf/contextualdata', require('./contextualdata.html')); diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 41be9667a18e..99e01d55a3c9 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -9,7 +9,9 @@ import * as olBase from 'ol/index.js'; import * as olEvents from 'ol/events.js'; import olCollection from 'ol/Collection.js'; import olFormatWFS from 'ol/format/WFS.js'; -import olInteractionSnap from 'ol/interaction/Snap.js'; +import olInteractionSnap, {handleEvent as snapHandleEvent} from 'ol/interaction/Snap.js'; +import {handleEvent as handlePointerEvent} from 'ol/interaction/Pointer.js'; + /** * The snapping service of GMF. Responsible of collecting the treeCtrls that @@ -139,19 +141,12 @@ class CustomSnap extends olInteractionSnap { document.body.addEventListener('keyup', () => { this.modifierPressed = false; }); - } - - handleEvent(evt) { - if (this.modifierPressed) { - return; - } else { - const result = this.snapTo(evt.pixel, evt.coordinate, evt.map); - if (result.snapped) { - evt.coordinate = result.vertex.slice(0, 2); - evt.pixel = result.vertexPixel; + this.handleEvent = (evt) => { // horrible hack + if (!this.modifierPressed) { + return snapHandleEvent.call(this, evt); } - return super.handleEvent(evt); - } + return handlePointerEvent.call(this, evt); + }; } } From 81ab8412394e471141d586b5cf03bfc66311b225 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 25 Jul 2019 09:55:59 +0200 Subject: [PATCH 08/10] Fix bad offline import --- src/offline/Configuration.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/offline/Configuration.js b/src/offline/Configuration.js index c3fba4565e1e..577dc8679a56 100644 --- a/src/offline/Configuration.js +++ b/src/offline/Configuration.js @@ -7,7 +7,6 @@ import olLayerVector from 'ol/layer/Vector.js'; import olLayerTile from 'ol/layer/Tile.js'; import olLayerImage from 'ol/layer/Image.js'; import * as olProj from 'ol/proj.js'; -import olSourceImage from 'ol/source/Image.js'; import olSourceImageWMS from 'ol/source/ImageWMS.js'; import olSourceTileWMS from 'ol/source/TileWMS.js'; import {createForProjection as createTileGridForProjection} from 'ol/tilegrid.js'; @@ -17,7 +16,7 @@ import LocalforageAndroidWrapper from 'ngeo/offline/LocalforageAndroidWrapper.js import LocalforageIosWrapper from 'ngeo/offline/LocalforageIosWrapper.js'; import ngeoCustomEvent from 'ngeo/CustomEvent.js'; import utils from 'ngeo/offline/utils.js'; -const defaultImageLoadFunction = olSourceImage.defaultImageLoadFunction; +import {defaultImageLoadFunction} from 'ol/source/Image.js'; import * as realLocalforage from 'localforage'; From bc2971d2f5f0691cb3681179066c25a0322bab0f Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Thu, 25 Jul 2019 10:38:14 +0200 Subject: [PATCH 09/10] Remove snap interaction hack --- contribs/gmf/src/editing/Snapping.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 99e01d55a3c9..0df5dbd36e6d 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -9,9 +9,7 @@ import * as olBase from 'ol/index.js'; import * as olEvents from 'ol/events.js'; import olCollection from 'ol/Collection.js'; import olFormatWFS from 'ol/format/WFS.js'; -import olInteractionSnap, {handleEvent as snapHandleEvent} from 'ol/interaction/Snap.js'; -import {handleEvent as handlePointerEvent} from 'ol/interaction/Pointer.js'; - +import olInteractionSnap from 'ol/interaction/Snap.js'; /** * The snapping service of GMF. Responsible of collecting the treeCtrls that @@ -134,19 +132,12 @@ const exports = function($http, $q, $rootScope, $injector, $timeout, gmfThemes, class CustomSnap extends olInteractionSnap { constructor(options) { super(options); - this.modifierPressed = false; document.body.addEventListener('keydown', (evt) => { - this.modifierPressed = evt.keyCode === 17; // Ctrl key + this.setActive(evt.keyCode !== 17); // Ctrl key }); document.body.addEventListener('keyup', () => { - this.modifierPressed = false; + this.setActive(true); }); - this.handleEvent = (evt) => { // horrible hack - if (!this.modifierPressed) { - return snapHandleEvent.call(this, evt); - } - return handlePointerEvent.call(this, evt); - }; } } From 1f1ac640a7b85a7d6fa25a6413e5bc400e40061a Mon Sep 17 00:00:00 2001 From: Laurent Lienher Date: Thu, 25 Jul 2019 13:45:22 +0200 Subject: [PATCH 10/10] Fix injector ngeoSnappingSource --- contribs/gmf/src/editing/Snapping.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gmf/src/editing/Snapping.js b/contribs/gmf/src/editing/Snapping.js index 0df5dbd36e6d..96a19e9c2413 100644 --- a/contribs/gmf/src/editing/Snapping.js +++ b/contribs/gmf/src/editing/Snapping.js @@ -124,7 +124,7 @@ const exports = function($http, $q, $rootScope, $injector, $timeout, gmfThemes, * @type {ol.source.Vector | undefined} * @private */ - this.ngeoSnappingSource_ = this.injector_.get('ngeoSnappingSource') || undefined; + this.ngeoSnappingSource_ = this.injector_.has('ngeoSnappingSource') ? this.injector_.get('ngeoSnappingSource') : undefined; };