Skip to content
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

Improve geocoder #4723

Merged
merged 35 commits into from Jan 5, 2017
Merged

Improve geocoder #4723

merged 35 commits into from Jan 5, 2017

Conversation

erikmaarten
Copy link
Contributor

@erikmaarten erikmaarten commented Dec 6, 2016

Adresses parts of #1262.

  • Support for custom geocoders
  • Support for automatic suggestions

Something I haven't quite decided on is whether there should be different methods for getting geocoder search suggestions and the actual geocoding. This would be necessary if a certain service has different API endpoints for this, or if they otherwise need to be handled differently. It would probably be better to keep this separate.

A custom geocoder is expected to look like:

{
    geocode: function(query) {...},
    getSuggestions: function(query) {...} // Currently not used -- should we have this?
}

The default geocoder in Cesium remains largely unchanged in this PR.

I've marked the GeocoderViewModel properties key and url for deprecation. I think these don't make sense to expose as public properties since they are implementation details (and may not exist) of the custom geocoder provided.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 7, 2016

I've marked the GeocoderViewModel properties key and url for deprecation.

Please submit an issue for this: https://github.com/AnalyticalGraphicsInc/cesium/tree/master/Documentation/Contributors/CodingGuide#deprecation-and-breaking-changes

bboxDegrees = resultObject.boundingbox;
return {
displayName: resultObject.display_name,
bbox: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bbox is part of the new public Cesium API, right? This is part of the "class" a geocode implementation returns.

It should following Cesium conventions:

  • Avoid abbreviations, probably call it rectangle
  • Use a Cesium type, probably Rectangle
  • Use radians. This will likely require most users to convert, e.g., using fromDegrees, but it will be consistent with the rest of the API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @pjcozzi. Yes, bbox will be part of the public API, I'll have this fixed.

@hpinkos
Copy link
Contributor

hpinkos commented Dec 7, 2016

@erikmaarten this is a great start, but I think there are a few changes we can make here to improve the API.

  • Create an GeocoderService interface that describes the properties and functions needed by a custom geocoding service (Feel free to use a better name if you can think of one. Unfortunately we're already using Geocoder for the widget name.) For example, here is the interface we created to describe an ImageryProvider: https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/ImageryProvider.js
  • Create BingMapsGeocoderService to abstract the bing maps geocoder functionality out of GeocoderViewModel
  • Make GeocoderViewModel take an optional GeocoderService parameter. If none is specified, use new BingMapsGeocoderService()
  • Possibly create OpenStreetMapGeocoderService using the functionality from your sandcastle example, then update the sandcastle example to pretty much be viewer.geocoder.geocoderService = new OpenStreetMapGeocoderService()

This will make it really easy to plug in different services in the future. For example, I know people on the forum were asking how to use the Mapbox geocoder as well. We could eventually add MapboxGeocoderService to make it easy for users to plug that in instead. This would also make it brainless to switch the default in case we ever decide to stop using Bing.

Also, if you could, it would be better to make the custom geocoder changes and the autocomplete changes in separate PRs so we can review each individually

@erikmaarten
Copy link
Contributor Author

@hpinkos Ready for review.

@erikmaarten erikmaarten mentioned this pull request Dec 8, 2016
)
};
});
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon


* Deprecated
* The properties `url` and `key` will be removed from `GeocoderViewModel` in 1.30. This properties will be available on geocoder services that support them, like `BingMapsGeocoderService`;
* Added support for custom geocoder services [4723](https://github.com/AnalyticalGraphicsInc/cesium/pull/4723).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a # before 4723

/**
* Provides geocoding through Bing Maps.
* @alias BingMapsGeocoderService
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add @params doc

options = defaultValue(options, defaultValue.EMPTY_OBJECT);
this._canceled = false;

this._url = 'https://dev.virtualearth.net/REST/v1/Locations';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be defaultValue(options.url, url)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it should be possible to supply a different URL, because then it's not really the same geocoder service anymore

*
*/
displayName : {
get : DeveloperError.throwInstantiationError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be here? It doesn't look like BingMapsGeocoderService uses this property

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't actually being used at the moment, so perhaps I should remove it, but I thought it would be useful to have a name for each service for the case where somebody wanted to show to end users which results come from which geocoders. But that may be a premature consideration since it's not actually used... what do you think, should I just get rid of it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this isn't being used, you should get rid of it. Thanks

@hpinkos
Copy link
Contributor

hpinkos commented Dec 16, 2016

@mramato ready for another review. I fix the problem you found, and also re-did the way the GeocoderViewModel was handling the promises for multiple geocoders. Instead of running geocoderService.geocode on each geocoder service, it treats it like a priority queue. It keeps asking down the chain until one of the geocoders returns a successful result.

@erikmaarten you should review the changes I made in f10c75f with regards to promises to see how I changed them together. Your use of when.all and creating an extra when.defer wasn't necessary, especially since in your last then you were just taking the first result returned.

@erikmaarten
Copy link
Contributor Author

Thanks @mramato for reviewing.

@hpinkos that's a nice refactor, but the functionality is not what I intended, if I'm reading the code correctly. It's good to avoid superfluous requests, but now users will be forced to wait for requests to start and fail in turn. If a more specifically scoped geocoder (like a geocoder with radio station codes as mentioned in the forums) is higher in priority, which I think would be normal, than a general geocoder, then users of that app would often have to wait for one extra network request (slow in the context of users interacting with the geocoder search bar), or more if more geocoders are used.

So while it makes technical sense to avoid requests that aren't necessary, doing the extra network requests upfront makes for better UX. A middle ground might also be sensible if we anticipate large numbers of geocoders in the same app: chaining the requests, but with chunks of five requests each time or something like that.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 20, 2016

Is this ready? If we want to ship this in 1.29, we should merge it this week.

@erikmaarten
Copy link
Contributor Author

@mramato had some comments on the interface design that I've replied to, and @hpinkos asked him for a follow-up review of the last changes, but since he's out for the holidays we won't be able to get any updates from him there.

@hpinkos have you seen my comments? Would like to hear what you think. Also, since my comments don't affect the interface, we could just settle with the current functionality and see how people actually use and then adapt to that.

@hpinkos
Copy link
Contributor

hpinkos commented Dec 21, 2016

It's good to avoid superfluous requests, but now users will be forced to wait for requests to start and fail in turn

I get what you're saying here. I did make it a bit more synchronous by waiting for the previous call to complete before making the next one. I was thinking the first one would often be the geocode that we ended up using, so I didn't see the point of making calls to the other geocoders and waiting for them all to either resolve or fail.

I'm really not sure which is best, so I'll wait to hear what @mramato thinks before changing things more

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 4, 2017

What is the plan with this pull request?

@hpinkos
Copy link
Contributor

hpinkos commented Jan 4, 2017

@mramato can you take a look at this?

@mramato
Copy link
Member

mramato commented Jan 4, 2017

On my list for later today.

@mramato
Copy link
Member

mramato commented Jan 4, 2017

  1. Start the Cesium Viewer
  2. Click on the magnifying glass (not the text box, just the icon part)
  3. Start typing and autocomplete doesn't trigger.

If you click inside the text field at any point, autocomplete starts to work as expected.

}
//>>includeEnd('debug');

try {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this has been covered, but what part of this try block can cause an exception?


var that = this;

this._suggestionsVisible = knockout.pureComputed(function () {
var suggestions = knockout.getObservable(that, '_suggestions');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My knockout-es5 is a little rusty, but is this knockout.getObservable needed here? I think you can just use that._suggestions directly and everything will work. CC @shunter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure it would trigger the pureComputed properly otherwise, but I didn't test it to see

*/
this.autoComplete = defaultValue(options.autocomplete, true);

this._focusTextbox = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be private if it's used in the bindings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every other thing we're binding to below is privage

}
});

GeocoderViewModel.prototype.destroy = function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add documentation.

viewModel._geocodePromise = promise;
promise
.then(function (result) {
if (promise.cancel || promise !== viewModel._geocodePromise) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the second condition here can ever be true, since it's impossible to start a second geocode while a current geocode is in process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be for if the geocode was triggered again before the first geocode resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but how is that possible with the code as currently written?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I was thinking it would happen if you hit the geocode button a few times, but in that case promise.cancel would be true. I just kept this here because that's how it was working before, but I agree that it doesn't seem necessary.

nextPromise.otherwise(function (err) {
return {state: 'rejected', reason: err};
});
} else if (defined(nextPromise.catch)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you are doing this to try and handle multiple promise implementations, but I think we should just simplify the code and always assume the promise was created with our version of when. Otherwise bad things can happen anyway, for example Bluebird promises have a cancel function, which would end up making your code think that cancel has been called on successful geocodes because you glob your own cancel onto the promise as a boolean. (hopefully that make sense).

We we upgrade to bluebird, these problems should go away, but we should just assume everyone is using Cesium's own when library for the time being.

@@ -2,7 +2,13 @@ Change Log
==========

### 1.29 - 2017-01-02

* Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge in master and move this to 1.30.

@mramato
Copy link
Member

mramato commented Jan 4, 2017

Thanks again @erikmaarten, I don't want to bikeshed this too much since it clearly works well (except in the couple cases I mentioned). Once my last round of comments are addressed we can merge this up to master.

@hpinkos
Copy link
Contributor

hpinkos commented Jan 4, 2017

@erikmaarten I'll take care of cleaning this up

@hpinkos
Copy link
Contributor

hpinkos commented Jan 4, 2017

@mramato ready

@erikmaarten
Copy link
Contributor Author

erikmaarten commented Jan 5, 2017

Thanks @hpinkos for taking care of this.

@mramato do you have any opinion on #4723 (comment)?

I updated the release numbers here to deprecate those properties in 1.30 and remove them in 1.31 since we didn't make it in time for the 1.29 release. I also updated the deprecation issue.

@mramato
Copy link
Member

mramato commented Jan 5, 2017

Thanks @hpinkos for taking care of this.

@mramato do you have any opinion on #4723 (comment)?

I updated the release numbers here to deprecate those properties in 1.30 and remove them in 1.31 since we didn't make it in time for the 1.29 release. I also updated the deprecation issue.

Ultimately, I think the use of multiple geocoders will be uncommon, so the point is probably moot. However, I agree that having a slow geocoder first could end up slowing down the entire operation in that use case, so firing multiple requests and letting the first one that returns "win" may be a good idea.

We should probably also make the limit of 5 an option in the view model so that users can allow more returns if desired.

I'm going to merge this as-is for now since it's in good shape, but if you (@erikmaarten) want to open another PR right away to add back the gecode "race" then we'll merge that too (and it will be easier to review since it will be a smaller change).

Thanks again!

@mramato mramato merged commit 4b612dc into CesiumGS:master Jan 5, 2017
@erikmaarten erikmaarten deleted the improve-geocoder branch January 5, 2017 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants