1717use Geocoder \Exception \InvalidCredentials ;
1818use Geocoder \Exception \QuotaExceeded ;
1919use Geocoder \Exception \UnsupportedOperation ;
20+ use Geocoder \Http \Provider \AbstractHttpProvider ;
2021use Geocoder \Model \AddressBuilder ;
2122use Geocoder \Model \AddressCollection ;
23+ use Geocoder \Provider \Here \Model \HereAddress ;
24+ use Geocoder \Provider \Provider ;
25+ use Geocoder \Query \Query ;
2226use Geocoder \Query \GeocodeQuery ;
2327use Geocoder \Query \ReverseQuery ;
24- use Geocoder \Http \Provider \AbstractHttpProvider ;
25- use Geocoder \Provider \Provider ;
26- use Geocoder \Provider \Here \Model \HereAddress ;
2728use Http \Client \HttpClient ;
2829
2930/**
@@ -34,22 +35,65 @@ final class Here extends AbstractHttpProvider implements Provider
3435 /**
3536 * @var string
3637 */
37- const GEOCODE_ENDPOINT_URL = 'https://geocoder.api.here.com/6.2/geocode.json?app_id=%s&app_code=%s&searchtext=%s&gen=9 ' ;
38+ const GEOCODE_ENDPOINT_URL_API_KEY = 'https://geocoder.ls.hereapi.com/6.2/geocode.json ' ;
39+
40+ /**
41+ * @var string
42+ */
43+ const GEOCODE_ENDPOINT_URL_APP_CODE = 'https://geocoder.api.here.com/6.2/geocode.json ' ;
44+
45+ /**
46+ * @var string
47+ */
48+ const GEOCODE_CIT_ENDPOINT_API_KEY = 'https:/geocoder.sit.ls.hereapi.com/6.2/geocode.json ' ;
3849
3950 /**
4051 * @var string
4152 */
42- const REVERSE_ENDPOINT_URL = 'https://reverse. geocoder.api.here.com/6.2/reversegeocode .json?prox=%F,%F,250&app_id=%s&app_code=%s&mode=retrieveAddresses&gen=9&maxresults=%d ' ;
53+ const GEOCODE_CIT_ENDPOINT_APP_CODE = 'https://geocoder.cit. api.here.com/6.2/geocode .json ' ;
4354
4455 /**
4556 * @var string
4657 */
47- const GEOCODE_CIT_ENDPOINT_URL = 'https://geocoder.cit.api.here. com/6.2/geocode .json?app_id=%s&app_code=%s&searchtext=%s&gen=9 ' ;
58+ const REVERSE_ENDPOINT_URL_API_KEY = 'https://reverse. geocoder.ls.hereapi. com/6.2/reversegeocode .json ' ;
4859
4960 /**
5061 * @var string
5162 */
52- const REVERSE_CIT_ENDPOINT_URL = 'https://reverse.geocoder.cit.api.here.com/6.2/reversegeocode.json?prox=%F,%F,250&app_id=%s&app_code=%s&mode=retrieveAddresses&gen=9&maxresults=%d ' ;
63+ const REVERSE_ENDPOINT_URL_APP_CODE = 'https://reverse.geocoder.api.here.com/6.2/reversegeocode.json ' ;
64+
65+ /**
66+ * @var string
67+ */
68+ const REVERSE_CIT_ENDPOINT_URL_API_KEY = 'https://reverse.geocoder.sit.ls.hereapi.com/6.2/reversegeocode.json ' ;
69+
70+ /**
71+ * @var string
72+ */
73+ const REVERSE_CIT_ENDPOINT_URL_APP_CODE = 'https://reverse.geocoder.cit.api.here.com/6.2/reversegeocode.json ' ;
74+
75+ /**
76+ * @var array
77+ */
78+ const GEOCODE_ADDITIONAL_DATA_PARAMS = [
79+ 'CrossingStreets ' ,
80+ 'PreserveUnitDesignators ' ,
81+ 'Country2 ' ,
82+ 'IncludeChildPOIs ' ,
83+ 'IncludeRoutingInformation ' ,
84+ 'AdditionalAddressProvider ' ,
85+ 'HouseNumberMode ' ,
86+ 'FlexibleAdminValues ' ,
87+ 'IntersectionSnapTolerance ' ,
88+ 'AddressRangeSqueezeOffset ' ,
89+ 'AddressRangeSqueezeFactor ' ,
90+ 'AddressRangeSqueezeOffset ' ,
91+ 'IncludeShapeLevel ' ,
92+ 'RestrictLevel ' ,
93+ 'SuppressStreetType ' ,
94+ 'NormalizeNames ' ,
95+ 'IncludeMicroPointAddresses ' ,
96+ ];
5397
5498 /**
5599 * @var string
@@ -66,24 +110,34 @@ final class Here extends AbstractHttpProvider implements Provider
66110 */
67111 private $ useCIT ;
68112
113+ /**
114+ * @var string
115+ */
116+ private $ apiKey ;
117+
69118 /**
70119 * @param HttpClient $client an HTTP adapter
71120 * @param string $appId an App ID
72121 * @param string $appCode an App code
73122 * @param bool $useCIT use Customer Integration Testing environment (CIT) instead of production
74123 */
75- public function __construct (HttpClient $ client , string $ appId , string $ appCode , bool $ useCIT = false )
124+ public function __construct (HttpClient $ client , string $ appId = null , string $ appCode = null , bool $ useCIT = false )
76125 {
77- if (empty ($ appId ) || empty ($ appCode )) {
78- throw new InvalidCredentials ('Invalid or missing api key. ' );
79- }
80126 $ this ->appId = $ appId ;
81127 $ this ->appCode = $ appCode ;
82128 $ this ->useCIT = $ useCIT ;
83129
84130 parent ::__construct ($ client );
85131 }
86132
133+ public static function createUsingApiKey (HttpClient $ client , string $ apiKey , bool $ useCIT = false ): self
134+ {
135+ $ client = new self ($ client , null , null , $ useCIT );
136+ $ client ->apiKey = $ apiKey ;
137+
138+ return $ client ;
139+ }
140+
87141 /**
88142 * {@inheritdoc}
89143 */
@@ -94,100 +148,33 @@ public function geocodeQuery(GeocodeQuery $query): Collection
94148 throw new UnsupportedOperation ('The Here provider does not support IP addresses, only street addresses. ' );
95149 }
96150
97- $ url = sprintf ($ this ->useCIT ? self ::GEOCODE_CIT_ENDPOINT_URL : self ::GEOCODE_ENDPOINT_URL , $ this ->appId , $ this ->appCode , rawurlencode ($ query ->getText ()));
151+ $ queryParams = $ this ->withApiCredentials ([
152+ 'searchtext ' => $ query ->getText (),
153+ 'gen ' => 9 ,
154+ 'additionaldata ' => $ this ->getAdditionalDataParam ($ query ),
155+ ]);
98156
99157 if (null !== $ query ->getData ('country ' )) {
100- $ url = sprintf ( ' %s& country=%s ' , $ url , rawurlencode ( $ query ->getData ('country ' )) );
158+ $ queryParams [ ' country ' ] = $ query ->getData ('country ' );
101159 }
102160
103161 if (null !== $ query ->getData ('state ' )) {
104- $ url = sprintf ( ' %s& state=%s ' , $ url , rawurlencode ( $ query ->getData ('state ' )) );
162+ $ queryParams [ ' state ' ] = $ query ->getData ('state ' );
105163 }
106164
107165 if (null !== $ query ->getData ('county ' )) {
108- $ url = sprintf ( ' %s& county=%s ' , $ url , rawurlencode ( $ query ->getData ('county ' )) );
166+ $ queryParams [ ' county ' ] = $ query ->getData ('county ' );
109167 }
110168
111169 if (null !== $ query ->getData ('city ' )) {
112- $ url = sprintf ( ' %s& city=%s ' , $ url , rawurlencode ( $ query ->getData ('city ' )) );
170+ $ queryParams [ ' city ' ] = $ query ->getData ('city ' );
113171 }
114172
115173 if (null !== $ query ->getLocale ()) {
116- $ url = sprintf ('%s&language=%s ' , $ url , $ query ->getLocale ());
117- }
118-
119- $ additionalDataParam = [];
120- if (null !== $ query ->getData ('CrossingStreets ' )) {
121- $ additionalDataParam ['CrossingStreets ' ] = $ query ->getData ('CrossingStreets ' );
122- }
123-
124- if (null !== $ query ->getData ('PreserveUnitDesignators ' )) {
125- $ additionalDataParam ['PreserveUnitDesignators ' ] = $ query ->getData ('PreserveUnitDesignators ' );
126- }
127-
128- if (null !== $ query ->getData ('Country2 ' )) {
129- $ additionalDataParam ['Country2 ' ] = $ query ->getData ('Country2 ' );
174+ $ queryParams ['language ' ] = $ query ->getLocale ();
130175 }
131176
132- if (null !== $ query ->getData ('IncludeChildPOIs ' )) {
133- $ additionalDataParam ['IncludeChildPOIs ' ] = $ query ->getData ('IncludeChildPOIs ' );
134- }
135-
136- if (null !== $ query ->getData ('IncludeRoutingInformation ' )) {
137- $ additionalDataParam ['IncludeRoutingInformation ' ] = $ query ->getData ('IncludeRoutingInformation ' );
138- }
139-
140- if (null !== $ query ->getData ('AdditionalAddressProvider ' )) {
141- $ additionalDataParam ['AdditionalAddressProvider ' ] = $ query ->getData ('AdditionalAddressProvider ' );
142- }
143-
144- if (null !== $ query ->getData ('HouseNumberMode ' )) {
145- $ additionalDataParam ['HouseNumberMode ' ] = $ query ->getData ('HouseNumberMode ' );
146- }
147-
148- if (null !== $ query ->getData ('FlexibleAdminValues ' )) {
149- $ additionalDataParam ['FlexibleAdminValues ' ] = $ query ->getData ('FlexibleAdminValues ' );
150- }
151-
152- if (null !== $ query ->getData ('IntersectionSnapTolerance ' )) {
153- $ additionalDataParam ['IntersectionSnapTolerance ' ] = $ query ->getData ('IntersectionSnapTolerance ' );
154- }
155-
156- if (null !== $ query ->getData ('AddressRangeSqueezeOffset ' )) {
157- $ additionalDataParam ['AddressRangeSqueezeOffset ' ] = $ query ->getData ('AddressRangeSqueezeOffset ' );
158- }
159-
160- if (null !== $ query ->getData ('AddressRangeSqueezeFactor ' )) {
161- $ additionalDataParam ['AddressRangeSqueezeFactor ' ] = $ query ->getData ('AddressRangeSqueezeFactor ' );
162- }
163-
164- if (null !== $ query ->getData ('IncludeShapeLevel ' )) {
165- $ additionalDataParam ['IncludeShapeLevel ' ] = $ query ->getData ('IncludeShapeLevel ' );
166- }
167-
168- if (null !== $ query ->getData ('RestrictLevel ' )) {
169- $ additionalDataParam ['RestrictLevel ' ] = $ query ->getData ('RestrictLevel ' );
170- }
171-
172- if (null !== $ query ->getData ('SuppressStreetType ' )) {
173- $ additionalDataParam ['SuppressStreetType ' ] = $ query ->getData ('SuppressStreetType ' );
174- }
175-
176- if (null !== $ query ->getData ('NormalizeNames ' )) {
177- $ additionalDataParam ['NormalizeNames ' ] = $ query ->getData ('NormalizeNames ' );
178- }
179-
180- if (null !== $ query ->getData ('IncludeMicroPointAddresses ' )) {
181- $ additionalDataParam ['IncludeMicroPointAddresses ' ] = $ query ->getData ('IncludeMicroPointAddresses ' );
182- }
183-
184- $ additionalDataParam ['IncludeShapeLevel ' ] = 'country ' ;
185-
186- if (!empty ($ additionalDataParam )) {
187- $ url = sprintf ('%s&additionaldata=%s ' , $ url , $ this ->serializeComponents ($ additionalDataParam ));
188- }
189-
190- return $ this ->executeQuery ($ url , $ query ->getLimit ());
177+ return $ this ->executeQuery (sprintf ('%s?%s ' , $ this ->getBaseUrl ($ query ), http_build_query ($ queryParams )), $ query ->getLimit ());
191178 }
192179
193180 /**
@@ -196,9 +183,15 @@ public function geocodeQuery(GeocodeQuery $query): Collection
196183 public function reverseQuery (ReverseQuery $ query ): Collection
197184 {
198185 $ coordinates = $ query ->getCoordinates ();
199- $ url = sprintf ($ this ->useCIT ? self ::REVERSE_CIT_ENDPOINT_URL : self ::REVERSE_ENDPOINT_URL , $ coordinates ->getLatitude (), $ coordinates ->getLongitude (), $ this ->appId , $ this ->appCode , $ query ->getLimit ());
200186
201- return $ this ->executeQuery ($ url , $ query ->getLimit ());
187+ $ queryParams = $ this ->withApiCredentials ([
188+ 'gen ' => 9 ,
189+ 'mode ' => 'retrieveAddresses ' ,
190+ 'prox ' => sprintf ('%s,%s ' , $ coordinates ->getLatitude (), $ coordinates ->getLongitude ()),
191+ 'maxresults ' => $ query ->getLimit (),
192+ ]);
193+
194+ return $ this ->executeQuery (sprintf ('%s?%s ' , $ this ->getBaseUrl ($ query ), http_build_query ($ queryParams )), $ query ->getLimit ());
202195 }
203196
204197 /**
@@ -210,6 +203,7 @@ public function reverseQuery(ReverseQuery $query): Collection
210203 private function executeQuery (string $ url , int $ limit ): Collection
211204 {
212205 $ content = $ this ->getUrlContents ($ url );
206+
213207 $ json = json_decode ($ content , true );
214208
215209 if (isset ($ json ['type ' ])) {
@@ -233,6 +227,8 @@ private function executeQuery(string $url, int $limit): Collection
233227
234228 $ locations = $ json ['Response ' ]['View ' ][0 ]['Result ' ];
235229
230+ $ results = [];
231+
236232 foreach ($ locations as $ loc ) {
237233 $ location = $ loc ['Location ' ];
238234 $ builder = new AddressBuilder ($ this ->getName ());
@@ -281,6 +277,73 @@ public function getName(): string
281277 return 'Here ' ;
282278 }
283279
280+ /**
281+ * Get serialized additional data param.
282+ *
283+ * @param GeocodeQuery $query
284+ *
285+ * @return string
286+ */
287+ private function getAdditionalDataParam (GeocodeQuery $ query ): string
288+ {
289+ $ additionalDataParams = [
290+ 'IncludeShapeLevel ' => 'country ' ,
291+ ];
292+
293+ foreach (self ::GEOCODE_ADDITIONAL_DATA_PARAMS as $ paramKey ) {
294+ if (null !== $ query ->getData ($ paramKey )) {
295+ $ additionalDataParams [$ paramKey ] = $ query ->getData ($ paramKey );
296+ }
297+ }
298+
299+ return $ this ->serializeComponents ($ additionalDataParams );
300+ }
301+
302+ /**
303+ * Add API credentials to query params.
304+ *
305+ * @param array $queryParams
306+ *
307+ * @return array
308+ */
309+ private function withApiCredentials (array $ queryParams ): array
310+ {
311+ if (
312+ empty ($ this ->apiKey ) &&
313+ (empty ($ this ->appId ) || empty ($ this ->appCode ))
314+ ) {
315+ throw new InvalidCredentials ('Invalid or missing api key. ' );
316+ }
317+
318+ if (null !== $ this ->apiKey ) {
319+ $ queryParams ['apiKey ' ] = $ this ->apiKey ;
320+ } else {
321+ $ queryParams ['app_id ' ] = $ this ->appId ;
322+ $ queryParams ['app_code ' ] = $ this ->appCode ;
323+ }
324+
325+ return $ queryParams ;
326+ }
327+
328+ public function getBaseUrl (Query $ query ): string
329+ {
330+ $ usingApiKey = null !== $ this ->apiKey ;
331+
332+ if ($ query instanceof ReverseQuery) {
333+ if ($ this ->useCIT ) {
334+ return $ usingApiKey ? self ::REVERSE_CIT_ENDPOINT_URL_API_KEY : self ::REVERSE_CIT_ENDPOINT_URL_APP_CODE ;
335+ }
336+
337+ return $ usingApiKey ? self ::REVERSE_ENDPOINT_URL_API_KEY : self ::REVERSE_ENDPOINT_URL_APP_CODE ;
338+ }
339+
340+ if ($ this ->useCIT ) {
341+ return $ usingApiKey ? self ::GEOCODE_CIT_ENDPOINT_API_KEY : self ::GEOCODE_CIT_ENDPOINT_APP_CODE ;
342+ }
343+
344+ return $ usingApiKey ? self ::GEOCODE_ENDPOINT_URL_API_KEY : self ::GEOCODE_ENDPOINT_URL_APP_CODE ;
345+ }
346+
284347 /**
285348 * Serialize the component query parameter.
286349 *
0 commit comments