Skip to content

Commit

Permalink
Fetch everything (#1229)
Browse files Browse the repository at this point in the history
* Fix Error on cellEnter

When zoomingOut, cellEnter was fired on FeatureGrid but was undefined

* fix travis error (spaces)

* add capability to fetch all features in a cell

* fix travis, failed test and add test

* merge proposal

* test for geojson for isModern

* support response.exceededTransferLimit or response.properties.exceededTransferLimit

Co-authored-by: francois <[email protected]>
Co-authored-by: François Charbonnier <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2020
1 parent 59ae703 commit 8c9c1e8
Show file tree
Hide file tree
Showing 3 changed files with 303 additions and 9 deletions.
285 changes: 284 additions & 1 deletion spec/Layers/FeatureLayer/FeatureManagerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,46 @@ describe('L.esri.FeatureManager', function () {
}
};

// geojson:
var feature7 = {
'type': 'Feature',
'geometry':
{
'type': 'Polygon',
'coordinates':
[
[
[-90.3038149502124, 38.6539545785218],
[-90.3038498654697, 38.6539303067945],
[-90.3038737094094, 38.6539138632284],
[-90.3039181787535, 38.6538794680055],
[-90.3042877084603, 38.6542092694323],
[-90.3042196068492, 38.6542595646522],
[-90.3041648638806, 38.6542969769789],
[-90.3038149502124, 38.6539545785218]
]
]
},
'properties': null
};

var feature8 = {
'type': 'Feature',
'geometry':
{
'type': 'Polygon',
'coordinates':
[
[
[-90.3038149502124, 38.6539545785218],
[-90.3038498654697, 38.6539303067945],
[-90.3038737094094, 38.6539138632284]
]
]
},
'properties': null
};

it('should be able to add itself to a map', function () {
layer.addTo(map);
expect(map.hasLayer(layer)).to.equal(true);
Expand Down Expand Up @@ -253,7 +293,6 @@ describe('L.esri.FeatureManager', function () {
layer.addTo(map);

server.respond();

expect(layer.createLayers).to.have.been.calledWith([
{
type: 'Feature',
Expand Down Expand Up @@ -1216,5 +1255,249 @@ describe('L.esri.FeatureManager', function () {
expect(layer._activeRequests).to.equal(0);
expect(triggered).to.be.true;
});

it('should fetch as geojson if "isModern" is true', function () {
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=0&resultType=tile&f=geojson',
JSON.stringify({
'type': 'FeatureCollection',
'features': [feature7]})
);

var layer = new MockLayer({
url:
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/',
fetchAllFeatures: true,
isModern: true
});

layer.addTo(map);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([feature7]);
});

it('should fetch another request if limit exceeded', function () {
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=0&resultType=tile&f=json',
JSON.stringify({
fields: fields,
features: [feature6],
objectIdFieldName: 'OBJECTID',
exceededTransferLimit: true
})
);

var layer = new MockLayer({
url:
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/',
fetchAllFeatures: true
});

layer.addTo(map);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[-109.02, 36.98],
[-102.06, 37.01],
[-102.06, 40.97],
[-109.02, 40.97],
[-109.02, 36.98]
]
]
},
properties: {
OBJECTID: 6,
Name: 'Site 6',
Type: 'Active',
StartTime: new Date('January 14 2014 GMT-0800').valueOf(),
EndTime: new Date('January 15 2014 GMT-0800').valueOf()
},
id: 6
}
]);

// second call due to fetchAllFeatures
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=1&resultType=tile&f=json',
JSON.stringify({
fields: fields,
features: [feature5],
objectIdFieldName: 'OBJECTID'
})
);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([
{
geometry: {
type: 'Point',
coordinates: [-122.629394, 45.537134]
},
id: 5,
properties: {
OBJECTID: 5,
Name: 'Site 5',
Type: 'Active',
StartTime: new Date('January 14 2014 GMT-0800').valueOf(),
EndTime: new Date('January 15 2014 GMT-0800').valueOf()
},
type: 'Feature'
}
]);
});

it('should fetch another request if limit exceeded - geojson exceededTransferLimit', function () {
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=0&resultType=tile&f=geojson',
JSON.stringify({
'type': 'FeatureCollection',
'features': [feature7],
'exceededTransferLimit': true
})
);

var layer = new MockLayer({
url:
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/',
fetchAllFeatures: true,
isModern: true
});

layer.addTo(map);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([feature7]);

// second call due to fetchAllFeatures
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=1&resultType=tile&f=geojson',
JSON.stringify({
'type': 'FeatureCollection',
'features': [feature8]
})
);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([feature8]);
});

it('should fetch another request if limit exceeded - geojson properties.exceededTransferLimit', function () {
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=0&resultType=tile&f=geojson',
JSON.stringify({
'type': 'FeatureCollection',
'features': [feature7],
'properties': {
'exceededTransferLimit': true
}
})
);

var layer = new MockLayer({
url:
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/',
fetchAllFeatures: true,
isModern: true
});

layer.addTo(map);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([feature7]);

// second call due to fetchAllFeatures
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=1&resultType=tile&f=geojson',
JSON.stringify({
'type': 'FeatureCollection',
'features': [feature8]
})
);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([feature8]);
});

it('should not fetch another request even if limit exceeded when no "fetchAllFeatures"', function () {
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultType=tile&f=json',
JSON.stringify({
fields: fields,
features: [feature6],
objectIdFieldName: 'OBJECTID',
exceededTransferLimit: true
})
);

var layer = new MockLayer({
url:
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/'
});

layer.addTo(map);
server.respond();
expect(layer.createLayers).to.have.been.calledWith([
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[-109.02, 36.98],
[-102.06, 37.01],
[-102.06, 40.97],
[-109.02, 40.97],
[-109.02, 36.98]
]
]
},
properties: {
OBJECTID: 6,
Name: 'Site 6',
Type: 'Active',
StartTime: new Date('January 14 2014 GMT-0800').valueOf(),
EndTime: new Date('January 15 2014 GMT-0800').valueOf()
},
id: 6
}
]);

// unexpected second call
server.respondWith(
'GET',
'http://gis.example.com/mock/arcgis/rest/services/MockService/MockFeatureServer/0/query?returnGeometry=true&where=1%3D1&outSR=4326&outFields=*&inSr=4326&geometry=%7B%22xmin%22%3A-122.6513671875%2C%22ymin%22%3A45.49094569262732%2C%22xmax%22%3A-122.607421875%2C%22ymax%22%3A45.521743896993634%2C%22spatialReference%22%3A%7B%22wkid%22%3A4326%7D%7D&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&geometryPrecision=6&resultOffset=1&resultType=tile&f=json',
JSON.stringify({
fields: fields,
features: [feature5],
objectIdFieldName: 'OBJECTID'
})
);
server.respond();
expect(layer.createLayers).not.to.have.been.calledWith([
{
geometry: {
type: 'Point',
coordinates: [-122.629394, 45.537134]
},
id: 5,
properties: {
OBJECTID: 5,
Name: 'Site 5',
Type: 'Active',
StartTime: new Date('January 14 2014 GMT-0800').valueOf(),
EndTime: new Date('January 15 2014 GMT-0800').valueOf()
},
type: 'Feature'
}
]);
});
});
/* eslint-enable handle-callback-err */
23 changes: 17 additions & 6 deletions src/Layers/FeatureLayer/FeatureManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export var FeatureManager = FeatureGrid.extend({
timeField: false,
timeFilterMode: 'server',
simplifyFactor: 0,
precision: 6
precision: 6,
fetchAllFeatures: false
},

/**
Expand Down Expand Up @@ -75,7 +76,7 @@ export var FeatureManager = FeatureGrid.extend({

// Check if someone has requested that we don't use geoJSON, even if it's available
var forceJsonFormat = false;
if (this.service.options.isModern === false) {
if (this.service.options.isModern === false || this.options.fetchAllFeatures) {
forceJsonFormat = true;
}

Expand Down Expand Up @@ -130,9 +131,12 @@ export var FeatureManager = FeatureGrid.extend({
}
},

_requestFeatures: function (bounds, coords, callback) {
_requestFeatures: function (bounds, coords, callback, offset) {
this._activeRequests++;

// default param
offset = offset || 0;

var originalWhere = this.options.where;

// our first active request fires loading
Expand All @@ -146,7 +150,7 @@ export var FeatureManager = FeatureGrid.extend({
);
}

return this._buildQuery(bounds).run(function (
return this._buildQuery(bounds, offset).run(function (
error,
featureCollection,
response
Expand Down Expand Up @@ -183,8 +187,11 @@ export var FeatureManager = FeatureGrid.extend({
if (callback) {
callback.call(this, error, featureCollection);
}
if (response && (response.exceededTransferLimit || (response.properties && response.properties.exceededTransferLimit)) && this.options.fetchAllFeatures) {
this._requestFeatures(bounds, coords, callback, offset + featureCollection.features.length);
}
},
this);
this);
},

_postProcessFeatures: function (bounds) {
Expand Down Expand Up @@ -228,14 +235,18 @@ export var FeatureManager = FeatureGrid.extend({
this.createLayers(features);
},

_buildQuery: function (bounds) {
_buildQuery: function (bounds, offset) {
var query = this.service
.query()
.intersects(bounds)
.where(this.options.where)
.fields(this.options.fields)
.precision(this.options.precision);

if (this.options.fetchAllFeatures && !isNaN(parseInt(offset))) {
query = query.offset(offset);
}

query.params['resultType'] = 'tile';

if (this.options.requestParams) {
Expand Down
4 changes: 2 additions & 2 deletions src/Tasks/Query.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ export var Query = Task.extend({
this._cleanParams();

// services hosted on ArcGIS Online and ArcGIS Server 10.3.1+ support requesting geojson directly
if (this.options.isModern || isArcgisOnline(this.options.url)) {
if (this.options.isModern || (isArcgisOnline(this.options.url) && this.options.isModern === undefined)) {
this.params.f = 'geojson';

return this.request(function (error, response) {
this._trapSQLerrors(error);
callback.call(context, error, response, response);
}, this);

// otherwise convert it in the callback then pass it on
// otherwise convert it in the callback then pass it on
} else {
return this.request(function (error, response) {
this._trapSQLerrors(error);
Expand Down

0 comments on commit 8c9c1e8

Please sign in to comment.