From 11f19a7c831e7595441b66ca43879794c6cac8fd Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 22 May 2024 12:46:47 -0700 Subject: [PATCH] feat: set labelingInfo via metadata (#1015) * feat: set labelingInfo via metadata * chore: increase test coverage --- .changeset/violet-fans-develop.md | 5 + package-lock.json | 22 +- packages/featureserver/coverage.svg | 8 +- packages/featureserver/package.json | 2 +- .../src/helpers/feature-layer-metadata.js | 50 ++- .../helpers/feature-layer-metadata.spec.js | 298 ++++++++---------- .../src/helpers/get-spatial-reference.js | 4 - .../src/helpers/get-spatial-reference.spec.js | 30 +- .../src/server-info-route-handler.js | 12 +- .../src/server-info-route-handler.spec.js | 4 +- packages/winnow/package.json | 1 - test/geoservice-layer-metadata.spec.js | 16 + .../points-w-metadata-labeling-info.geojson | 25 ++ 13 files changed, 260 insertions(+), 217 deletions(-) create mode 100644 .changeset/violet-fans-develop.md create mode 100644 test/provider-data/points-w-metadata-labeling-info.geojson diff --git a/.changeset/violet-fans-develop.md b/.changeset/violet-fans-develop.md new file mode 100644 index 000000000..89eb0d6b2 --- /dev/null +++ b/.changeset/violet-fans-develop.md @@ -0,0 +1,5 @@ +--- +"@koopjs/featureserver": minor +--- + +- set labelingInfo via metadata diff --git a/package-lock.json b/package-lock.json index df6e76b84..713febece 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5081,14 +5081,6 @@ "version": "2.1.2", "license": "MIT" }, - "node_modules/@terraformer/spatial": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@terraformer/spatial/-/spatial-2.2.1.tgz", - "integrity": "sha512-Zq2WbKJgZspurToBJ7KjmmHC7HbvNt/fH8Bh/kj+RgcGoM9OzurAXlEfIMrVQQIh3ypSbJn+hHQ33n/d805uBQ==", - "dependencies": { - "@terraformer/common": "^2.1.2" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "dev": true, @@ -22224,7 +22216,7 @@ "@esri/proj-codes": "^3.3.0", "@koopjs/logger": "5.0.0", "@koopjs/winnow": "5.0.2", - "@terraformer/spatial": "^2.2.1", + "@turf/envelope": "^6.5.0", "chroma-js": "^2.4.2", "esri-extent": "^1.1.3", "geojson-validation": "^1.0.2", @@ -22276,7 +22268,6 @@ "@esri/proj-codes": "^3.3.0", "@koopjs/logger": "5.0.0", "@terraformer/arcgis": "^2.1.2", - "@terraformer/spatial": "^2.2.1", "@turf/bbox-polygon": "^6.5.0", "@turf/boolean-contains": "^6.5.0", "@turf/boolean-equal": "^6.5.0", @@ -24649,7 +24640,7 @@ "@esri/proj-codes": "^3.3.0", "@koopjs/logger": "5.0.0", "@koopjs/winnow": "5.0.2", - "@terraformer/spatial": "^2.2.1", + "@turf/envelope": "^6.5.0", "chroma-js": "^2.4.2", "esri-extent": "^1.1.3", "even-more-min": "^1.0.1", @@ -24723,7 +24714,6 @@ "@esri/proj-codes": "^3.3.0", "@koopjs/logger": "5.0.0", "@terraformer/arcgis": "^2.1.2", - "@terraformer/spatial": "^2.2.1", "@turf/bbox-polygon": "^6.5.0", "@turf/boolean-contains": "^6.5.0", "@turf/boolean-equal": "^6.5.0", @@ -26076,14 +26066,6 @@ "@terraformer/common": { "version": "2.1.2" }, - "@terraformer/spatial": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@terraformer/spatial/-/spatial-2.2.1.tgz", - "integrity": "sha512-Zq2WbKJgZspurToBJ7KjmmHC7HbvNt/fH8Bh/kj+RgcGoM9OzurAXlEfIMrVQQIh3ypSbJn+hHQ33n/d805uBQ==", - "requires": { - "@terraformer/common": "^2.1.2" - } - }, "@tootallnate/once": { "version": "1.1.2", "dev": true diff --git a/packages/featureserver/coverage.svg b/packages/featureserver/coverage.svg index c08c6584c..4630d7bba 100644 --- a/packages/featureserver/coverage.svg +++ b/packages/featureserver/coverage.svg @@ -1,5 +1,5 @@ - - coverage: 98.11% + + coverage: 98.58% @@ -13,8 +13,8 @@ \ No newline at end of file diff --git a/packages/featureserver/package.json b/packages/featureserver/package.json index 74030867a..a47a943e3 100644 --- a/packages/featureserver/package.json +++ b/packages/featureserver/package.json @@ -23,7 +23,7 @@ "@esri/proj-codes": "^3.3.0", "@koopjs/logger": "5.0.0", "@koopjs/winnow": "5.0.2", - "@terraformer/spatial": "^2.2.1", + "@turf/envelope": "^6.5.0", "chroma-js": "^2.4.2", "esri-extent": "^1.1.3", "geojson-validation": "^1.0.2", diff --git a/packages/featureserver/src/helpers/feature-layer-metadata.js b/packages/featureserver/src/helpers/feature-layer-metadata.js index 51e743ab7..9b850271e 100644 --- a/packages/featureserver/src/helpers/feature-layer-metadata.js +++ b/packages/featureserver/src/helpers/feature-layer-metadata.js @@ -1,7 +1,7 @@ const _ = require('lodash'); const TableLayerMetadata = require('./table-layer-metadata'); const { PointRenderer, LineRenderer, PolygonRenderer } = require('./renderers'); -const { calculateBounds } = require('@terraformer/spatial'); +const envelope = require('@turf/envelope'); const logManager = require('../log-manager'); const getSpatialReference = require('./get-spatial-reference'); const getGeometryTypeFromGeojson = require('./get-geometry-type-from-geojson'); @@ -22,32 +22,34 @@ class FeatureLayerMetadata extends TableLayerMetadata { return this; } - mixinOverrides(geojson = {}, options = {}) { + mixinOverrides(geojson, options = {}) { super.mixinOverrides(geojson, options); - const { renderer, extent, inputCrs, sourceSR, capabilities = {} } = options; + const { renderer, labelingInfo, extent, inputCrs, sourceSR, capabilities = {} } = options; this.geometryType = getGeometryTypeFromGeojson({ ...geojson, ...options }); this.supportsCoordinatesQuantization = !!capabilities.quantization; - this._setExtent(geojson, { inputCrs, sourceSR, extent }); + this.#_setExtent(geojson, { inputCrs, sourceSR, extent }); - this._setRenderer(renderer); + this.#_setRenderer(renderer); - this._setDirectOverrides(options); + this.#_setLabelingInfo(labelingInfo); + + this.#_setDirectOverrides(options); return this; } - _setExtent(geojson, options) { + #_setExtent(geojson, options) { const extent = getLayerExtent(geojson, options); if (extent) { this.extent = extent; } } - _setRenderer(renderer) { + #_setRenderer(renderer) { if (renderer) { this.drawingInfo.renderer = renderer; return; @@ -55,14 +57,24 @@ class FeatureLayerMetadata extends TableLayerMetadata { if (this.geometryType === 'esriGeometryPolygon') { this.drawingInfo.renderer = new PolygonRenderer(); - } else if (this.geometryType === 'esriGeometryPolyline') { + return; + } + + if (this.geometryType === 'esriGeometryPolyline') { this.drawingInfo.renderer = new LineRenderer(); - } else { - this.drawingInfo.renderer = new PointRenderer(); + return; } + + this.drawingInfo.renderer = new PointRenderer(); } - _setDirectOverrides(options) { + #_setLabelingInfo(labelingInfo) { + if (labelingInfo) { + this.drawingInfo.labelingInfo = labelingInfo; + } + } + + #_setDirectOverrides(options) { super._setDirectOverrides(options); const { minScale, maxScale } = options; @@ -74,10 +86,7 @@ class FeatureLayerMetadata extends TableLayerMetadata { } function getLayerExtent(geojson, options) { - const spatialReference = getSpatialReference(geojson, options) || { - wkid: 4326, - latestWkid: 4326, - }; + const spatialReference = getSpatialReference(geojson, options); const { extent } = options; @@ -94,8 +103,15 @@ function calculateExtentFromFeatures(geojson, spatialReference) { } try { - const [xmin, ymin, xmax, ymax] = calculateBounds(geojson); + const { bbox } = envelope(geojson); + + bbox.forEach((coordinate) => { + if (!isFinite(coordinate)) { + throw new Error(`Feature does not contain valid geometry`); + } + }); + const [xmin, ymin, xmax, ymax] = bbox; return { xmin, xmax, diff --git a/packages/featureserver/src/helpers/feature-layer-metadata.spec.js b/packages/featureserver/src/helpers/feature-layer-metadata.spec.js index 93fc3d5d9..1edb8d70f 100644 --- a/packages/featureserver/src/helpers/feature-layer-metadata.spec.js +++ b/packages/featureserver/src/helpers/feature-layer-metadata.spec.js @@ -4,17 +4,8 @@ const sinon = require('sinon'); const proxyquire = require('proxyquire'); const CURRENT_VERSION = 11.2; -const calculateBoundsSpy = sinon.spy(function () { - return { - spatialReference: { - latestWkid: 4326, - wkid: 4326, - }, - xmax: 180, - xmin: -180, - ymax: 90, - ymin: -90, - }; +const envelopeSpy = sinon.spy(function () { + return { bbox: [-180, -90, 180, 90] }; }); const getSpatialReferenceSpy = sinon.spy(function () { return { @@ -22,17 +13,15 @@ const getSpatialReferenceSpy = sinon.spy(function () { latestWkid: 4326, }; }); -const getGeometryTypeFromGeojsonSpy = sinon.spy(function () { - return 'esriGeometryPoint'; +const getGeometryTypeFromGeojsonSpy = sinon.spy(function (options) { + return options.geometryType || 'esriGeometryPoint'; }); const normalizeExtentSpy = sinon.spy(function () { return 'normalized-extent'; }); const FeatureLayerMetadata = proxyquire('./feature-layer-metadata', { - '@terraformer/spatial': { - calculateBounds: calculateBoundsSpy, - }, + '@turf/envelope': envelopeSpy, './get-spatial-reference': getSpatialReferenceSpy, './get-geometry-type-from-geojson': getGeometryTypeFromGeojsonSpy, './normalize-extent': normalizeExtentSpy, @@ -40,7 +29,7 @@ const FeatureLayerMetadata = proxyquire('./feature-layer-metadata', { describe('FeatureLayerMetadata', () => { beforeEach(() => { - calculateBoundsSpy.resetHistory(); + envelopeSpy.resetHistory(); getSpatialReferenceSpy.resetHistory(); getGeometryTypeFromGeojsonSpy.resetHistory(); normalizeExtentSpy.resetHistory(); @@ -170,6 +159,16 @@ describe('FeatureLayerMetadata', () => { }); describe('mixinOverrides', () => { + it('should function without options', () => { + const featureLayerMetadata = new FeatureLayerMetadata(); + + const result = featureLayerMetadata.mixinOverrides({ + features: [], + }); + + result.should.be.an.Object(); + }); + it('should set point geometryType and renderer', () => { const featureLayerMetadata = new FeatureLayerMetadata(); featureLayerMetadata.mixinOverrides( @@ -179,158 +178,90 @@ describe('FeatureLayerMetadata', () => { { foo: 'bar' }, ); - featureLayerMetadata.should.deepEqual({ - currentVersion: CURRENT_VERSION, - supportedPbfFeatureEncodings: 'esriDefault', - id: 0, - name: 'Not Set', - type: 'Feature Layer', - displayField: 'OBJECTID', - description: - 'This is a feature layer exposed with Koop. For more information go to https://github.com/koopjs/koop.', // eslint-disable-line - copyrightText: - 'Copyright information varies by provider. For more information please contact the source of this data.', // eslint-disable-line - defaultVisibility: true, - isDataVersioned: false, - hasContingentValuesDefinition: false, - supportsAppend: false, - supportsCalculate: false, - supportsASyncCalculate: false, - supportsTruncate: false, - supportsAttachmentsByUploadId: false, - supportsAttachmentsResizing: false, - supportsRollbackOnFailureParameter: false, - supportsStatistics: true, - supportsExceedsLimitStatistics: false, - supportsAdvancedQueries: true, - supportsValidateSql: false, - supportsLayerOverrides: false, - supportsTilesAndBasicQueriesMode: true, - supportsFieldDescriptionProperty: false, - supportsQuantizationEditMode: false, - supportsApplyEditsWithGlobalIds: false, - supportsReturningQueryGeometry: false, - advancedQueryCapabilities: { - supportsPagination: true, - supportsQueryAttachmentsCountOnly: false, - supportsPaginationOnAggregatedQueries: false, - supportsQueryRelatedPagination: false, - supportsQueryWithDistance: false, - supportsReturningQueryExtent: true, - supportsStatistics: true, - supportsOrderBy: true, - supportsDistinct: true, - supportsQueryWithResultType: false, - supportsSqlExpression: false, - supportsAdvancedQueryRelated: false, - supportsCountDistinct: false, - supportsPercentileStatistics: false, - supportedSpatialAggregationStatistics: [], - supportsLod: false, - supportsQueryWithLodSR: false, - supportedLodTypes: [], - supportsReturningGeometryCentroid: false, - supportsReturningGeometryEnvelope: false, - supportsQueryWithDatumTransformation: false, - supportsCurrentUserQueries: false, - supportsHavingClause: false, - supportsOutFieldSQLExpression: false, - supportsMaxRecordCountFactor: false, - supportsTopFeaturesQuery: false, - supportsDisjointSpatialRel: false, - supportsQueryWithCacheHint: false, - supportedOperationsWithCacheHint: [], - supportsQueryAnalytic: false, - supportsDefaultSR: false, - supportsFullTextSearch: false, - advancedQueryAnalyticCapabilities: {}, - advancedEditingCapabilities: {}, + featureLayerMetadata.drawingInfo.renderer.should.deepEqual({ + type: 'simple', + symbol: { + color: [247, 150, 70, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', }, - useStandardizedQueries: true, - allowGeometryUpdates: false, - hasAttachments: false, - htmlPopupType: 'esriServerHTMLPopupTypeNone', - hasM: false, - hasZ: false, - objectIdField: 'OBJECTID', - uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, - globalIdField: '', - typeIdField: '', - dateFieldsTimeReference: { - timeZone: 'UTC', - respectsDaylightSaving: false, + }); + featureLayerMetadata.geometryType.should.equal('esriGeometryPoint'); + getGeometryTypeFromGeojsonSpy.callCount.should.equal(1); + getGeometryTypeFromGeojsonSpy.firstCall.args.should.deepEqual([ + { + features: [], + foo: 'bar', }, - preferredTimeReference: null, - templates: [], - supportedQueryFormats: 'JSON,geojson,PBF', - supportedAppendFormats: '', - supportedExportFormats: '', - supportedSpatialRelationships: [ - 'esriSpatialRelIntersects', - 'esriSpatialRelContains', - 'esriSpatialRelEnvelopeIntersects', - 'esriSpatialRelWithin', - ], - supportedContingentValuesFormats: '', - hasStaticData: false, - maxRecordCount: 2000, - standardMaxRecordCount: 2000, - standardMaxRecordCountNoGeometry: 2000, - tileMaxRecordCount: 2000, - maxRecordCountFactor: 1, - fields: [ - { - name: 'OBJECTID', - type: 'esriFieldTypeOID', - alias: 'OBJECTID', - sqlType: 'sqlTypeInteger', - domain: null, - defaultValue: null, - editable: false, - nullable: false, - }, - ], - relationships: [], - capabilities: 'Query', - ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, - types: [], - timeInfo: {}, - minScale: 0, - maxScale: 0, - drawingInfo: { - renderer: { - type: 'simple', - symbol: { - color: [247, 150, 70, 161], - outline: { - color: [190, 190, 190, 105], - width: 0.5, - type: 'esriSLS', - style: 'esriSLSSolid', - }, - size: 7.5, - type: 'esriSMS', - style: 'esriSMSCircle', - }, - }, - labelingInfo: null, + ]); + }); + + it('should set line geometryType and renderer', () => { + const featureLayerMetadata = new FeatureLayerMetadata(); + featureLayerMetadata.mixinOverrides( + { + features: [], }, - extent: { - xmin: -180, - ymin: -90, - xmax: 180, - ymax: 90, - spatialReference: { wkid: 4326, latestWkid: 4326 }, + { foo: 'bar', geometryType: 'esriGeometryPolyline' }, + ); + + featureLayerMetadata.drawingInfo.renderer.should.deepEqual({ + type: 'simple', + symbol: { + color: [247, 150, 70, 204], + width: 6.999999999999999, + type: 'esriSLS', + style: 'esriSLSSolid', }, - supportsCoordinatesQuantization: false, - hasLabels: false, - geometryType: 'esriGeometryPoint', }); + featureLayerMetadata.geometryType.should.equal('esriGeometryPolyline'); getGeometryTypeFromGeojsonSpy.callCount.should.equal(1); getGeometryTypeFromGeojsonSpy.firstCall.args.should.deepEqual([ { features: [], foo: 'bar', + geometryType: 'esriGeometryPolyline', + }, + ]); + }); + + it('should set polygon geometryType and renderer', () => { + const featureLayerMetadata = new FeatureLayerMetadata(); + featureLayerMetadata.mixinOverrides( + { + features: [], + }, + { foo: 'bar', geometryType: 'esriGeometryPolygon' }, + ); + + featureLayerMetadata.drawingInfo.renderer.should.deepEqual({ + type: 'simple', + symbol: { + color: [75, 172, 198, 161], + outline: { + color: [150, 150, 150, 155], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + type: 'esriSFS', + style: 'esriSFSSolid', + }, + }); + featureLayerMetadata.geometryType.should.equal('esriGeometryPolygon'); + getGeometryTypeFromGeojsonSpy.callCount.should.equal(1); + getGeometryTypeFromGeojsonSpy.firstCall.args.should.deepEqual([ + { + features: [], + foo: 'bar', + geometryType: 'esriGeometryPolygon', }, ]); }); @@ -652,7 +583,7 @@ describe('FeatureLayerMetadata', () => { }); getSpatialReferenceSpy.callCount.should.equal(1); - calculateBoundsSpy.callCount.should.equal(0); + envelopeSpy.callCount.should.equal(0); normalizeExtentSpy.callCount.should.equal(1); }); @@ -815,7 +746,39 @@ describe('FeatureLayerMetadata', () => { }); getSpatialReferenceSpy.callCount.should.equal(1); - calculateBoundsSpy.callCount.should.equal(1); + envelopeSpy.callCount.should.equal(1); + normalizeExtentSpy.callCount.should.equal(0); + }); + + it('should fail to set extent from features', () => { + const envelopeSpy = sinon.spy(function () { + return { bbox: [-180, Infinity, 180, 90] }; + }); + const FeatureLayerMetadata = proxyquire('./feature-layer-metadata', { + '@turf/envelope': envelopeSpy, + './get-spatial-reference': getSpatialReferenceSpy, + './get-geometry-type-from-geojson': getGeometryTypeFromGeojsonSpy, + './normalize-extent': normalizeExtentSpy, + }); + const featureLayerMetadata = new FeatureLayerMetadata(); + + featureLayerMetadata.mixinOverrides( + { + features: ['feature'], + }, + {}, + ); + + featureLayerMetadata.extent.should.deepEqual({ + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }); + + getSpatialReferenceSpy.callCount.should.equal(1); + envelopeSpy.callCount.should.equal(1); normalizeExtentSpy.callCount.should.equal(0); }); @@ -961,6 +924,19 @@ describe('FeatureLayerMetadata', () => { }); }); + it('should set labelingInfo from options', () => { + const featureLayerMetadata = new FeatureLayerMetadata(); + + featureLayerMetadata.mixinOverrides( + { + features: [], + }, + { labelingInfo: 'label-info' }, + ); + + featureLayerMetadata.drawingInfo.labelingInfo.should.equal('label-info'); + }); + it('should set other direct overrides', () => { const featureLayerMetadata = new FeatureLayerMetadata(); diff --git a/packages/featureserver/src/helpers/get-spatial-reference.js b/packages/featureserver/src/helpers/get-spatial-reference.js index 84ad4987f..c9a716222 100644 --- a/packages/featureserver/src/helpers/get-spatial-reference.js +++ b/packages/featureserver/src/helpers/get-spatial-reference.js @@ -1,15 +1,11 @@ -const _ = require('lodash'); const getCollectionCrs = require('./get-collection-crs'); const normalizeSpatialReference = require('./normalize-spatial-reference'); function getSpatialReference(geojson, { inputCrs, sourceSR } = {}) { - if (!inputCrs && !sourceSR && _.isEmpty(geojson)) return; const spatialReference = inputCrs || sourceSR || getCollectionCrs(geojson) || { wkid: 4326, latestWkid: 4326 }; - if (!spatialReference) return; - const { latestWkid, wkid, wkt } = normalizeSpatialReference(spatialReference); if (wkid) { diff --git a/packages/featureserver/src/helpers/get-spatial-reference.spec.js b/packages/featureserver/src/helpers/get-spatial-reference.spec.js index 2db8341f5..7f41ae95a 100644 --- a/packages/featureserver/src/helpers/get-spatial-reference.spec.js +++ b/packages/featureserver/src/helpers/get-spatial-reference.spec.js @@ -4,7 +4,7 @@ const getSpatialReference = require('./get-spatial-reference'); describe('get-spatial-reference', () => { it('getSpatialReference: no data passed', () => { const wkt = getSpatialReference(); - should(wkt).equal(undefined); + should(wkt).deepEqual({ wkid: 4326, latestWkid: 4326 }); }); it('getSpatialReference: only inputCrs', () => { @@ -14,6 +14,29 @@ describe('get-spatial-reference', () => { should(wkt).deepEqual({ wkid: 4326, latestWkid: 4326 }); }); + it('getSpatialReference: only inputCrs as wkt', () => { + const wkt = `PROJCS["NAD_1983_StatePlane_California_V_FIPS_0405_Feet", + GEOGCS["GCS_North_American_1983", + DATUM["North_American_Datum_1983", + SPHEROID["GRS_1980",6378137,298.257222101]], + PRIMEM["Greenwich",0], + UNIT["Degree",0.017453292519943295]], + PROJECTION["Lambert_Conformal_Conic_2SP"], + PARAMETER["False_Easting",6561666.666666666], + PARAMETER["False_Northing",1640416.666666667], + PARAMETER["Central_Meridian",-118], + PARAMETER["Standard_Parallel_1",34.03333333333333], + PARAMETER["Standard_Parallel_2",35.46666666666667], + PARAMETER["Latitude_Of_Origin",33.5], + UNIT["Foot_US",0.30480060960121924]]`; + + const sr = getSpatialReference(undefined, { + inputCrs: { + wkt, + }, + }); + should(sr).deepEqual({ wkt }); + }); it('getSpatialReference: numeric inputCrs', () => { const wkt = getSpatialReference(undefined, { inputCrs: 4326 }); should(wkt).deepEqual({ wkid: 4326, latestWkid: 4326 }); @@ -36,6 +59,11 @@ describe('get-spatial-reference', () => { should(wkt).deepEqual({ wkid: 3857, latestWkid: 3857 }); }); + it('getSpatialReference: none, use default', () => { + const wkt = getSpatialReference({ foo: 'bar' }); + should(wkt).deepEqual({ wkid: 4326, latestWkid: 4326 }); + }); + it('getSpatialReference: all inputs available', () => { const wkt = getSpatialReference( { crs: { properties: { name: 'epsg:3857' } } }, diff --git a/packages/featureserver/src/server-info-route-handler.js b/packages/featureserver/src/server-info-route-handler.js index d8ca7f207..8fda48424 100644 --- a/packages/featureserver/src/server-info-route-handler.js +++ b/packages/featureserver/src/server-info-route-handler.js @@ -1,5 +1,5 @@ const _ = require('lodash'); -const { calculateBounds } = require('@terraformer/spatial'); +const envelope = require('@turf/envelope'); const { getCollectionCrs, getGeometryTypeFromGeojson, @@ -86,13 +86,13 @@ function calculateServiceExtentFromLayers(layers, spatialReference) { return _.has(layer, 'features[0]'); }) .map((layer) => { - const bounds = calculateBounds(layer); - bounds.forEach((coordinate) => { - if (isNaN(coordinate)) { - throw new Error(`Geometry coordinate is not a number: ${coordinate}`); + const { bbox } = envelope(layer); + bbox.forEach((coordinate) => { + if (!isFinite(coordinate)) { + throw new Error(`Feature does not contain valid geometry`); } }); - return bounds; + return bbox; }) .reduce( (accumulator, bounds) => { diff --git a/packages/featureserver/src/server-info-route-handler.spec.js b/packages/featureserver/src/server-info-route-handler.spec.js index 407a5134a..153cdb590 100644 --- a/packages/featureserver/src/server-info-route-handler.spec.js +++ b/packages/featureserver/src/server-info-route-handler.spec.js @@ -186,7 +186,7 @@ describe('server info', () => { ]); }); - it('should construct options from feature collection with CRS and features, no settings', () => { + it('should construct options from collection with CRS and features, no settings', () => { const simpleCollectionFixture = { type: 'FeatureCollection', crs: { @@ -610,7 +610,7 @@ describe('server info', () => { { type: 'Feature', properties: {}, - geometry: { type: 'Point', coordinates: ['test', 40] }, + geometry: { type: 'Point', coordinates: ['test', undefined] }, }, ], }; diff --git a/packages/winnow/package.json b/packages/winnow/package.json index fee35687c..bd75270ea 100644 --- a/packages/winnow/package.json +++ b/packages/winnow/package.json @@ -47,7 +47,6 @@ "@esri/proj-codes": "^3.3.0", "@koopjs/logger": "5.0.0", "@terraformer/arcgis": "^2.1.2", - "@terraformer/spatial": "^2.2.1", "@turf/bbox-polygon": "^6.5.0", "@turf/boolean-contains": "^6.5.0", "@turf/boolean-equal": "^6.5.0", diff --git a/test/geoservice-layer-metadata.spec.js b/test/geoservice-layer-metadata.spec.js index 190d983c9..a015020f6 100644 --- a/test/geoservice-layer-metadata.spec.js +++ b/test/geoservice-layer-metadata.spec.js @@ -27,5 +27,21 @@ describe('koop', () => { throw error; } }); + + test('handles labelingInfo override', async () => { + try { + const response = await request(koop.server).get( + '/file-geojson/rest/services/points-w-metadata-labeling-info/FeatureServer/0', + ); + expect(response.status).toBe(200); + const { + drawingInfo: { labelingInfo }, + } = response.body; + expect(labelingInfo).toBe('label-spec'); + } catch (error) { + console.error(error); + throw error; + } + }); }); }); diff --git a/test/provider-data/points-w-metadata-labeling-info.geojson b/test/provider-data/points-w-metadata-labeling-info.geojson new file mode 100644 index 000000000..401d261dd --- /dev/null +++ b/test/provider-data/points-w-metadata-labeling-info.geojson @@ -0,0 +1,25 @@ +{ + "type": "FeatureCollection", + "metadata": { + "idField": "id", + "labelingInfo": "label-spec" + }, + "features": [ + { + "type": "Feature", + "properties": { + "id": "aaa", + "timestamp": "2023-04-10T16:15:30.000Z", + "label": "White Leg", + "category": "pinto" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -80, + 25 + ] + } + } + ] +} \ No newline at end of file