Problem/Motivation
Several geocoder providers can use, or even require, additional parameters that are stored in the data part of the GeocodeQuery accessed with getData and setData methods. If you are building a site, or coding, knowing which gecoders are to used this is useful (to essential depending on gecoder and site requirements).
The use case I've run into this is for restricting results by countrycodes (which of course you know when someone has filled in an address field as it's the first thing they have to do) for Nominatim but a short grep threw up plenty of more use cases:-
TomTom: setData('countrySet')
Yandex: (reverseQuery) setData('toponym')
PickPoint: (reverseQuery) setData('zoom') required (defaults to 18)
Nominatim: setData('countrycodes') setData('viewbox') setData('bounded')
(reverseQuery) setData('zoom') required (defaults to 18)
OpenCage: setData('countrycode') setData('proximity')
also getBounds()
LocationIQ: (reverseQuery) setData('zoon') required defaults to 18
Mapbox: setData('location_type') setData('fuzzyMatch') defaults to false setData('country)'
GoogleMaps: setData('components') required setData('region') defaults to null (can be set on construction)
GoogleMapsPlaces: (both) setData('mode') required defaults to 'find'
AlgoliaPlaces: setData('type') and setData('countries') required no defaults
Here: 'country', 'state', 'county', 'city'; and open 'additionaldata'
Proposed resolution
The resolution, tasks, and API changes, for this this can be the same as #3406299: Content language by being able to construct the GeocodeQuery that is passed. Both get fixed with the same solution.
| Comment | File | Size | Author |
|---|
Issue fork geocoder-3406303
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
itamair commentedThanks @ekes ... indeed this is a nice insight and input. Good ideas for nice enhancements of the Geocoder module.
Comment #3
ekes commentedSomething like this?
https://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34...
I'd kinda like a way of telling, as a consumer of the services, which type of plugin (instanceof Provider, or instanceof GeometryProviderInterface) it is; but I couldn't quite think of a way of separating them further without breaking backward compatibility. So this is trying to integrate with what's there in the same way it's working now.
Comment #4
progga commentedThanks Ekes. I have tested the code and it works as expected.
ProviderUsingHandlerBase is missing a `use` statement for the `ProviderGeocoderPhpInterface` interface. Otherwise all good.I was thinking it would be good if the `geocoder` service could accept GeocodeQuery. The attached patch for the fork achieves that.
Comment #5
progga commentedCorrected patch for the fork attached.
Comment #6
ekes commented> I was thinking it would be good if the `geocoder` service could accept GeocodeQuery.
I was trying to think of a way of making the change there backward compatible and that does seem to do it.
Comment #7
ekes commentedAdded to the branch.
Comment #8
progga commentedThanks. Tested with a few Geocoders and worked as expected. No errors or warnings in the Drupal log. RTBC.
Comment #9
ekes commentedComment #10
ekes commentedI've now got code using this, and it's doing just what's wanted there too: https://git.drupalcode.org/project/geo_entity/-/blob/4bd966f02827113f4ed...
Comment #11
itamair commentedThanks a lot here. I am following and will take my chance to double check and test this, before committing into dev ...
Comment #12
finn lewisHey itamair,
any chance of rolling this into a release?
We could use it in LocalGov Drupal.
Many thanks,
Finn
Comment #16
itamair commentedSwell ... actually I had to make some fixes to #5 patch with an additional commit,
some lack of quality in that implementation:
i.e.: the ProviderGeocoderPhpInterface not really existing anywhere ...
then I QAed positively and all this was worth to be deployed.
Both 8.x-4.19 and 8.x-3.42 new releases are now implementing this.
Comment #17
ekes commentedI'll review this, but I think what's been committed is missing a lot of the patch that is required. Including that interface!
Comment #18
progga commentedThanks for merging. And sorry for causing the confusion. I should have been clearer. The patch was for Eke's fork. So it was more like an interdiff. Please accept Eke's pull request for full functionality.
Comment #19
itamair commentedAh ok ... sorry, my fault, but also some confusion here. If there is a MR open, patches don't make much sense in the thread (but only confusion) isn't it?
Comment #20
itamair commentedPlease @ekes adjust the MR to the actual 8.x-4.19 head ...
Comment #21
progga commented> patches don't make much sense in the thread (but only confusion) isn't it?
Yeah, I should have been smarter :(
Comment #25
itamair commentedFYI ... I rolled / reverted everything here with a new 8.x-4.20 release that brings everything before the commits mentioned in this thread
Comment #27
ekes commentedOK so `3406303-pass-additional-parameters` was the original, now merged again with 4.x at 4.20; and `3406303-additional-parameters-update` was my reapplying (I think correctly) to the updated 4.x with the two commits that were on 4.19, but that's not needed now. However for certainty you should see that there is no diff between the two branches (just different commits) :-)
Both branches should have both my original geocodeQuery stuff, and progga's addition that makes it work from the geocoder class directly.
Branch `3406303-pass-additional-parameters` that applies to current 4.x HEAD will be the one you want now though.
Comment #29
itamair commentedThanks @ekes got it ...
Actually I tested locally (it is working fine ... ) the `3406303-pass-additional-parameters` branch and spot some / many refinements (but all small coding standards, some typos, etc. nothing more ... ) that need to be added to it.
Thus ... could you open a MR on that? so I can commit my refinement and also proceed with my code review with some further comments
(i.e. the tests/modules/gecoder_test_provider/src/Geocoder/Provider/MockProvider.php functions are all missing documentation ... etc.).
Being open I am still a bit reluctant in merging / committing all this, because it is quite a lot of new code and I couldn't really test this new functionality and use case ...
BUT I am also inclined to trust your skills and match your needs (as a Local Gov Team).
Besides the MR and my further (little) comments on that, could you just answer to the following question of mine, to make me more confident and sure about all this:
Thanks! again ...
Comment #30
ekes commentedHere goes:
Backward compatibility
The public method
Geocoder::geocodedoes change its signature but only adding that it also accepts aGeocodeQueryin addition to astringhttps://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34...The reported change https://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34... in the
ProviderBase::processis in fact one that was already the case before this patch, again it is additional, it was already also returning an Address Collection or Geometry. This is also the same fordoGecodeanddoReverse. It's a Collection if the geocoder is from PHP-Geocoder https://github.com/geocoder-php/Geocoder/blob/9c2224fc81be843c5c8c3e8925... and Geometry if it's from geofield https://git.drupalcode.org/project/geocoder/-/blob/8.x-4.x/modules/geoco...Added functionality
So in addition to the additional
GeocodeQueryargument type forGeocoder::geocodemethod at the Provider level it adds explicit methods to use if you have constructed aGeocodeQueryyourself and want to run it directly with a specific geocoder provider. This makes sense because you often want to add particular specific arguments for specific geocoders in theGeocodeQuery. These are defined by the interface https://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34... which any provider supportingGeocodeQuerywill implement. This is basically all of the PHP-Gecoder providers that return theAdressCollection, and not theGeometryProviderInterfacebased ones.You see the interface and new methods in use in the
Geocoder::geocodemethod https://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34... where it decides only to pass a GeocodeQuery into the provider if it implements the interfaceChanges to how code is working internally
To enable reuse of the caching mechanism the methods to store have been spun out from
ProviderBase::process. The internal methodsProviderBase::getCachehttps://git.drupalcode.org/issue/geocoder-3406303/-/blob/3406303-pass-ad... and::setCachehttps://git.drupalcode.org/issue/geocoder-3406303/-/blob/3406303-pass-ad... are called in theProviderBase::processhttps://git.drupalcode.org/issue/geocoder-3406303/-/blob/3406303-pass-ad... and it will operate in the same way as it did with the array containing a single string, or lon and lat https://git.drupalcode.org/project/geocoder/-/blob/8.x-4.x/src/ProviderB...But now these methods are available for
geocodeQueryhttps://git.drupalcode.org/issue/geocoder-3406303/-/blob/3406303-pass-ad... andreverseQueryhttps://git.drupalcode.org/issue/geocoder-3406303/-/blob/3406303-pass-ad... where they can pass in aGeocodeQuery. The reason to reuse the code in a method, in addition to DRY, was it would be easier to change the caching in the future as it's all in one method that everyone can use.Testing
The test https://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34... call the Mock Provider https://git.drupalcode.org/issue/geocoder-3406303/-/blob/3406303-pass-ad... which if it the code described above is correct will get called and return a standard response. This is checking that the Provider's methods are actually getting called correctly if you are using the
::geocode,::geocodeQueryetc. methods.The additional tests https://git.drupalcode.org/issue/geocoder-3406303/-/compare/8.x-4.x...34... check that the
Geocoder::geocodemethod does not break if aGeocodeQueryis passed when the provider doesn't support it; and it checks that it does return something when it does.Usage
In the geo_entity module example also mentioned above https://git.drupalcode.org/project/geo_entity/-/blob/4bd966f02827113f4ed... the address and the country codes are made available as geodata, which are then used to customize the query depending on the provider.
For example for Nominatim it restricts the results to the country using its 'countrycodes' parameter https://git.drupalcode.org/project/geo_entity/-/blob/4bd966f02827113f4ed... No longer get results from the United States that include the string 'GB' when you know someone has created an address in Great Britain!
There are more optional parameters like the custodian_code for the https://github.com/localgovdrupal/localgov_os_places_geocoder_provider OS Places Geocoder that @progga has written. I listed other above in the motivation for this.
Passing these arguments in a
GeocodeQuerytoGeocoder::gecodewhere it runs multiple Providers is also safe, because the Providers ignore any arguments they don't know.Comment #32
itamair commentedWoooooow ... @ekes, you are really outstanding.
Thousand thanks for your time in giving all those insights.
Definitely I will be happy to approve dan merge this asap.
I pushed my refinements / corrections to the MR. Please review my last commit and eventually tell me if I was wrong in something
(but I simply trusted my phpstorm drupal coding standards reporting, etc.).
Also I opened a Code review with one update/change request here: https://git.drupalcode.org/project/geocoder/-/merge_requests/45#note_258270
Comment #33
ekes commentedComment #36
itamair commentedHere we go ... thanks a lot @ekes, and everybody else here.
I just added another small commit on some tiny phpcs and merged both into 4.x and 3.x branch ...
All this will be part of the new incoming Geocoder release.
Comment #42
ekes commented🎉 nice one. Thanks.
Comment #43
itamair commentedComment #44
progga commentedGreat to see this merged :)