-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OGC Features Provider and Features Layer #12493
base: main
Are you sure you want to change the base?
Conversation
Thank you for the pull request, @jjspace! ✅ We can confirm we have a CLA on file for you. |
* @param {Object} options | ||
*/ | ||
function FeatureProvider(options) { | ||
this.id = options.id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've had some pain points with inheritance in the past. To avoid these, I would recommend treating FeatureProvider
as an interface as much as possible. That is, I would avoid defining any implementation details, and leave it to the implementor or static utility-like functions.
// this._itemsLoaded < this.maxItems | ||
itemsLoadedThisRequest < this.maxItems | ||
) { | ||
if (localAbortController.signal.aborted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify my understanding, the abort controller as it is used in this class is not actively cancelling an in-progress requests— Only preventing more requests from happening. Is that correct?
As a follow up question, is this something important we need to consider for the Resource
implementation? Would it fit in with the existing implementation, or would it necessitate more significant refactoring?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify my understanding, the abort controller as it is used in this class is not actively cancelling an in-progress requests— Only preventing more requests from happening. Is that correct?
Correct, as currently written this just stops the loop and future requests from starting and ignores the results. I implemented this first and it basically is just acting as a nicer wrapper for toggling a boolean for previous calls to this function. I added the cancelResource
function later to try and actually handle canceling the underlying network requests themselves and prevent the browser itself from blocking. There's probably a cleaner way to do this but "it worked" for now.
The AbortController
and signal is designed to actually be used with the native fetch
(I picked up the pattern when working on a vscode extension). fetch
has an option to pass the signal
in when it's created and it will just handle canceling the underlying request. This won't work for us because Resource
is still built around XHR requests which do not have this functionality.
|
||
Object.defineProperties(FeatureProvider.prototype, {}); | ||
|
||
FeatureProvider.prototype.requestFeatures = function (bbox, time) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if the bbox
parameter should be a bit more generic. While bbox is very straightforward for the OGC features API, it may not map to other providers as well such as something more tile-based.
I'm trying to think through what may be needed to determine the proper request... Perhaps camera (position, direction, and frustum extents), scene mode, ellipsoid... I'm trying to avoid something as low-level as passing in the FrameState
as we do in many places, but that may be a possibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in person I believe this will be replaced with the full camera
object so any implementations can calculate a bbox or other features from that.
/** | ||
* A class for things | ||
* @constructor | ||
* @param {FeatureProvider | OGCFeaturesProvider} provider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If OGCFeaturesProvider
implements FeatureProvieder
, I don't think we'd need to specify the type explicitly, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically no. However as previously discussed at length TS will not recognize inheritance that's defined purely through JSDoc so it doesn't understand this without both. I have been adding JSDoc comments for types to help me while I develop but may need to do a pass on all of them before this is merged to clean them up to be "correct"
), | ||
); | ||
|
||
if (distance > 400) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are camera moved events on Camera.js
. Perhaps they are sufficient?
OgcFeatureProvider.prototype.requestFeatures = async function ({ | ||
bbox, | ||
time, | ||
partialLoadCallback, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be defined in FeatureProvider
even if it's a no-op for certain implementations.
Also consider exposing events on FeatureProvider
like featuresLoaded
which can be subscribed to by the FeatureLayer
.
/** | ||
* @param {Feature} feature | ||
*/ | ||
FeatureLayer.prototype.add = function (feature) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll probably want remove
and removeAll
functions as well.
// this._primitives = this._dataSource.entities; | ||
this._primitives = []; | ||
|
||
this.features = options.features ?? new AssociativeArray(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find an issue for it, but AssociativeArray
can probably be replaced with native Map
at this point. It's easy an efficient to pull the values as an array through built-in methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, and perhaps more importantly, we should be smart about how we store data for lots and lots of features. There's potential for a lot of duplication across the graphical representation (whether than be Entities or Primitives) and a raw array. We likely want to release the raw features once the representations have been created.
this.id = options.id; | ||
// this.name = options.name; | ||
this._show = options.show ?? true; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this defines a Rectangle
, we should likely be specifying an ellipsoid at this granularity as well so it's meaningful to map the extents to worldspace positions.
* | ||
* @param {GeoJson} geoJson | ||
* @param {ParseOptions} [options] | ||
* @returns {Entity | Entity[] | undefined} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it would be possible to map this to a list of Feature
objects? And that, in turn, those could be mapped to Entities in another function to be used GeoJsonDataSource
?
That way, the future implementation could map the returned features to primitives.
// this.name = options.name; | ||
this._show = options.show ?? true; | ||
|
||
this.rectangle = options.rectangle ?? new Rectangle(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this rectangle intended to be the extent of the data, or a user defined restriction?
I think these should be separate properties, and the former should probably be read-only via the public API.
*/ | ||
function FeatureProvider(options) { | ||
this.id = options.id; | ||
this.credit = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be implemented such that when the data is in view, credits are added on screen or in the lightbox.
I'm not sure what you plan was here, so I'm just stating this explicitly.
Description
Opening as a draft for early review and feedback around implementation. Will fill out the description more later.
See #12456
Also worth taking a look at @ggetz's
feature-prototype
branch for how we might do some of the lower level stuff.Issue number and link
Resolves #12456
Testing plan
OgcFeatureProvider
andFeatureLayer
classesAuthor checklist
CONTRIBUTORS.md
CHANGES.md
with a short summary of my change