From 7285b53acde7e3ae09370170fb93baf3767d96dd Mon Sep 17 00:00:00 2001 From: malong Date: Sat, 20 Jul 2019 09:51:40 +0800 Subject: [PATCH 1/3] GeometryCollection type support added --- src/api.js | 17 ++-- src/feature_types/feature.js | 11 +++ src/feature_types/geometryCollection.js | 115 ++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 src/feature_types/geometryCollection.js diff --git a/src/api.js b/src/api.js index 66ed58701..43597473f 100644 --- a/src/api.js +++ b/src/api.js @@ -13,7 +13,8 @@ const featureTypes = { Point: require('./feature_types/point'), MultiPolygon: require('./feature_types/multi_feature'), MultiLineString: require('./feature_types/multi_feature'), - MultiPoint: require('./feature_types/multi_feature') + MultiPoint: require('./feature_types/multi_feature'), + GeometryCollection: require('./feature_types/geometryCollection').default }; module.exports = function(ctx, api) { @@ -25,18 +26,18 @@ module.exports = function(ctx, api) { return features.map(feature => feature.properties.id); }; - api.getSelectedIds = function () { + api.getSelectedIds = function() { return ctx.store.getSelectedIds(); }; - api.getSelected = function () { + api.getSelected = function() { return { type: Constants.geojsonTypes.FEATURE_COLLECTION, features: ctx.store.getSelectedIds().map(id => ctx.store.get(id)).map(feature => feature.toGeoJSON()) }; }; - api.getSelectedPoints = function () { + api.getSelectedPoints = function() { return { type: Constants.geojsonTypes.FEATURE_COLLECTION, features: ctx.store.getSelectedCoordinates().map(coordinate => { @@ -70,7 +71,7 @@ module.exports = function(ctx, api) { return newIds; }; - api.add = function (geojson) { + api.add = function(geojson) { const errors = geojsonhint.hint(geojson, { precisionWarning: false }).filter(e => e.level !== 'message'); if (errors.length) { throw new Error(errors[0].message); @@ -96,8 +97,8 @@ module.exports = function(ctx, api) { // If a feature of that id has already been created, and we are swapping it out ... const internalFeature = ctx.store.get(feature.id); internalFeature.properties = feature.properties; - if (!isEqual(internalFeature.getCoordinates(), feature.geometry.coordinates)) { - internalFeature.incomingCoords(feature.geometry.coordinates); + if (!internalFeature.isEqual(feature)) { + internalFeature.update(feature); } } return feature.id; @@ -108,7 +109,7 @@ module.exports = function(ctx, api) { }; - api.get = function (id) { + api.get = function(id) { const feature = ctx.store.get(id); if (feature) { return feature.toGeoJSON(); diff --git a/src/feature_types/feature.js b/src/feature_types/feature.js index 5065104c6..a056db6a1 100644 --- a/src/feature_types/feature.js +++ b/src/feature_types/feature.js @@ -1,5 +1,6 @@ const hat = require('hat'); const Constants = require('../constants'); +const isEqual = require('lodash.isequal'); const Feature = function(ctx, geojson) { this.ctx = ctx; @@ -67,4 +68,14 @@ Feature.prototype.internal = function(mode) { }; }; +Feature.prototype.isEqual = function(feature) { + return isEqual(this.getCoordinates(), feature.geometry.coordinates); +}; + +Feature.prototype.update = function(feature) { + if (!this.isEqual(feature)) { + this.incomingCoords(feature.geometry.coordinates); + } +}; + module.exports = Feature; diff --git a/src/feature_types/geometryCollection.js b/src/feature_types/geometryCollection.js new file mode 100644 index 000000000..ae0279f7b --- /dev/null +++ b/src/feature_types/geometryCollection.js @@ -0,0 +1,115 @@ +import Constants from '../constants'; +import hat from 'hat'; +const isEqual = require('lodash.isequal'); + +const models = { + Point: require('./point'), + LineString: require('./line_string'), + Polygon: require('./polygon') +}; + +const GeometryCollection = function(ctx, geojson) { + this.ctx = ctx; + this.id = geojson.id || hat(); + this.type = 'GeometryCollection'; + this.properties = geojson.properties; + this.features = geojson.geometry.geometries.map(g => { + return this._coordinatesToFeatures(g); + }); +}; +GeometryCollection.prototype._coordinatesToFeatures = function(geometry) { + const model = models[geometry.type]; + if (model === undefined) throw new TypeError(`${geometry.type} is not a valid type`); + + const Model = model.bind(this); + return new Model(this.ctx, { + id: hat(), + type: Constants.geojsonTypes.FEATURE, + properties: {}, + geometry + }); +}; +GeometryCollection.prototype.changed = function() { + this.ctx.store.featureChanged(this.id); +}; +GeometryCollection.prototype.isValid = function() { + return this.features.every(f => f.isValid()); +}; +GeometryCollection.prototype.incomingCoords = function() { + throw Error('incomingCoords'); +}; +GeometryCollection.prototype.setCoordinates = function() { + throw Error('setCoordinates'); +}; +GeometryCollection.prototype.getCoordinates = function() { + throw Error('getCoordinates'); +}; +GeometryCollection.prototype.setProperty = function(property, value) { + this.properties[property] = value; +}; +GeometryCollection.prototype.toGeoJSON = function() { + const result = JSON.parse(JSON.stringify({ + id: this.id, + type: Constants.geojsonTypes.FEATURE, + properties: this.properties, + geometry: { + geometries: this.features.map(f => { + return { + type: f.type, + coordinates: f.getCoordinates() + }; + }), + type: this.type + } + })); + return result; +}; +GeometryCollection.prototype.internal = function(mode) { + const properties = { + id: this.id, + meta: Constants.meta.FEATURE, + 'meta:type': this.type, + active: Constants.activeStates.INACTIVE, + mode: mode + }; + + if (this.ctx.options.userProperties) { + for (const name in this.properties) { + properties[`user_${name}`] = this.properties[name]; + } + } + const result = { + type: Constants.geojsonTypes.FEATURE, + properties: properties, + geometry: { + type: this.type, + geometries: this.features.map(f => { + return { + type: f.type, + coordinates: f.getCoordinates() + }; + }) + } + }; + return result; +}; + +GeometryCollection.prototype.isEqual = function(feature) { + const { geometries } = feature.geometry; + return this.features.every((myf, index) => { + return isEqual(myf.getCoordinates(), geometries[index].coordinates); + }); +}; + +GeometryCollection.prototype.update = function(feature) { + const { geometries } = feature.geometry; + this.features.forEach((myf, index) => { + const coordinates = geometries[index].coordinates; + if (!isEqual(myf.getCoordinates(), coordinates)) { + myf.incomingCoords(coordinates); + } + }); +}; + +export default GeometryCollection; + From 995690739054b4357b3ec47ba7f309e0988c551e Mon Sep 17 00:00:00 2001 From: malong Date: Sat, 20 Jul 2019 10:03:21 +0800 Subject: [PATCH 2/3] fix: 'isEqual' is assigned a value but never used --- src/api.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api.js b/src/api.js index 43597473f..34c7135af 100644 --- a/src/api.js +++ b/src/api.js @@ -1,4 +1,3 @@ -const isEqual = require('lodash.isequal'); const normalize = require('@mapbox/geojson-normalize'); const hat = require('hat'); const featuresAt = require('./lib/features_at'); From a1573106dc93c9c30b4996f9a512f7a1d3fe5483 Mon Sep 17 00:00:00 2001 From: malong Date: Sat, 20 Jul 2019 10:24:09 +0800 Subject: [PATCH 3/3] fix Feature.prototype length error --- test/feature.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/feature.test.js b/test/feature.test.js index c88c7d8fa..c60c37d00 100644 --- a/test/feature.test.js +++ b/test/feature.test.js @@ -26,7 +26,7 @@ test('Feature contrusctor and API', t => { t.equal(typeof Feature.prototype.toGeoJSON, 'function', 'feature.toGeoJSON'); t.equal(typeof Feature.prototype.internal, 'function', 'feature.internal'); t.equal(typeof Feature.prototype.setProperty, 'function', 'feature.setProperty'); - t.equal(getPublicMemberKeys(Feature.prototype).length, 7, 'no unexpected prototype members'); + t.equal(getPublicMemberKeys(Feature.prototype).length, 9, 'no unexpected prototype members'); const simpleFeatureGeoJson = { type: 'Feature',