2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <maps_coordinates.h>
21 #include "mapzen_jsonparser.hpp"
22 #include "rapidjson/document.h"
25 #include "mapzen_queue.h"
26 #include "mapzen_debug.h"
27 #include "mapzen_util.h"
37 #define ROUTE_UNIT_CONVERSION_MILE_TO_M(x) (1609.34 * (x))
38 #define ROUTE_UNIT_CONVERSION_MILE_TO_KM(x) (1.60934 * (x))
39 #define ROUTE_UNIT_CONVERSION_MILE_TO_FT(x) (5280 * (x))
40 #define ROUTE_UNIT_CONVERSION_MILE_TO_YD(x) (1760 * (x))
42 static route_unit __route_unit = UNIT_M;
43 static int __maneuver_index = 0;
44 static coords_s __destination_point;
45 constexpr double kPolylinePrecision = 1E6;
46 constexpr double kInvPolylinePrecision = 1.0 / kPolylinePrecision;
49 //TODO: port this to c? or port the whole project to c++ or add all the proper externs
50 void decode_shape(const std::string& encoded,
51 std::vector<maps_coordinates_s>& output)
53 // what byte are we looking at
56 // Handy lambda to turn a few bytes of an encoded string into an integer
57 auto deserialize = [&encoded, &i](const int previous) {
58 // Grab each 5 bits and mask it in where it belongs using the shift
59 int byte, shift = 0, result = 0;
61 byte = static_cast<int>(encoded[i++]) - 63;
62 result |= (byte & 0x1f) << shift;
64 } while (byte >= 0x20);
66 // Undo the left shift from above or the bit flipping and add
67 // to previous since its an offset
68 return previous + (result & 1 ? ~(result >> 1) : (result >> 1));
71 // Iterate over all characters of the encoded shape string
72 int last_lon = 0, last_lat = 0;
73 while (i < encoded.length())
75 // Decode the coordinates, lat first
76 int lat = deserialize(last_lat);
77 int lon = deserialize(last_lon);
79 // Shift the decimal point 5 places to the left and add to the output
80 maps_coordinates_s ll;
81 ll.latitude = static_cast<double>(lat) * kInvPolylinePrecision;
82 ll.longitude = static_cast<double>(lon) * kInvPolylinePrecision;
83 output.emplace_back(std::move(ll));
85 // Remember the last one we encountered
92 static mapzen_error_e __convert_status(int status)
94 mapzen_error_e error = MAPZEN_ERROR_UNKNOWN;
98 /* Successful Geocode call */
99 error = MAPZEN_ERROR_NONE;
104 /* Error with input - Illegal argument from request */
105 error = MAPZEN_ERROR_INVALID_PARAMETER;
110 /* Key related error - Invalid key */
111 error = MAPZEN_ERROR_KEY_NOT_AVAILABLE;
117 error = MAPZEN_ERROR_UNKNOWN;
123 error = MAPZEN_ERROR_NETWORK_UNREACHABLE;
131 bool __get_string(const rapidjson::Value& obj, const char* key, char** value)
133 //if we have the key and its not an empty value
134 rapidjson::Value::ConstMemberIterator itr = obj.FindMember(key);
135 if(itr != obj.MemberEnd() && itr->value.GetStringLength()){
136 (*value) = (gchar *)g_malloc(itr->value.GetStringLength() + sizeof(rapidjson::Document::Ch));
137 strncpy((*value), itr->value.GetString(), itr->value.GetStringLength());
138 memset((*value) + itr->value.GetStringLength(), 0, sizeof(rapidjson::Document::Ch));
144 return *value != NULL;
147 /************ GEOCODE ***************/
150 static void __parse_geocode_response(char *response, int size, int *status, GList **coordsList)
152 MAP_DEBUG("Inside __parse_geocode_response..");
155 if (!response || !status || !coordsList) return;
157 //so far we have nothing
161 //crack open that json
162 rapidjson::Document document;
163 document.Parse(std::string(response, size).c_str());
164 rapidjson::Value::ConstMemberIterator features = document.FindMember("features");
165 if(features != document.MemberEnd() && features->value.IsArray()) {
167 for (rapidjson::Value::ConstValueIterator f = features->value.Begin(); f != features->value.End(); ++f) {
168 //it has to have geometry
169 rapidjson::Value::ConstMemberIterator geom = f->FindMember("geometry");
170 if(geom != f->MemberEnd()) {
172 rapidjson::Value::ConstMemberIterator type = geom->value.FindMember("type");
173 if(type == geom->value.MemberEnd() || strcmp(type->value.GetString(), "Point"))
176 rapidjson::Value::ConstMemberIterator coords = geom->value.FindMember("coordinates");
177 if(coords == geom->value.MemberEnd() || !coords->value.IsArray() || coords->value.Size() != 2)
180 coords_s *coord = (coords_s *)g_malloc0(sizeof(coords_s));
182 coord->longitude = coords->value[0].GetDouble();
183 coord->latitude = coords->value[1].GetDouble();
186 if (*coordsList == NULL)
187 *coordsList = g_list_append(*coordsList, coord);
189 *coordsList = g_list_insert_before(*coordsList, NULL, coord);
196 /****************** REVERSE GEOCODE *********************/
198 static void __parse_revgeocode_response(char *response, int size, int *status, mapzen_address_resp_s **respAddr)
200 if (!response || !status || !respAddr) return;
202 //so far we have nothing
206 //crack open that json
207 rapidjson::Document document;
208 document.Parse(std::string(response, size).c_str());
209 rapidjson::Value::ConstMemberIterator features = document.FindMember("features");
210 if(features != document.MemberEnd() && features->value.IsArray()) {
212 for (rapidjson::Value::ConstValueIterator f = features->value.Begin(); f != features->value.End(); ++f) {
213 //it has to have geometry
214 rapidjson::Value::ConstMemberIterator geom = f->FindMember("geometry");
215 if(geom != f->MemberEnd()) {
217 rapidjson::Value::ConstMemberIterator type = geom->value.FindMember("type");
218 if(type == geom->value.MemberEnd() || strcmp(type->value.GetString(), "Point"))
221 rapidjson::Value::ConstMemberIterator coords = geom->value.FindMember("coordinates");
222 if(coords == geom->value.MemberEnd() || !coords->value.IsArray() || coords->value.Size() != 2)
225 //NOTE: it seems as though the tizen maps plugin api assumes that reverse geocode results will
226 //actually be at the coordinates where you requested the reverse geocode. the mapzen api can return
227 //POIS which are some distance away from the location and currently this is not accounted for :o(
229 //get out the address information
230 rapidjson::Value::ConstMemberIterator properties = f->FindMember("properties");
231 if(properties != f->MemberEnd()) {
232 //fill this out as we go
233 *respAddr = (mapzen_address_resp_s *)g_malloc(sizeof(mapzen_address_resp_s));
234 bool something = false;
235 something = __get_string(properties->value, "street", &(*respAddr)->street) || something;
236 something = __get_string(properties->value, "neighbourhood", &(*respAddr)->neighbourhood) || something;
237 something = __get_string(properties->value, "housenumber", &(*respAddr)->housenumber) || something;
238 something = __get_string(properties->value, "localadmin", &(*respAddr)->localadmin) || something;
239 something = __get_string(properties->value, "county", &(*respAddr)->county) || something;
240 something = __get_string(properties->value, "region", &(*respAddr)->region) || something;
241 something = __get_string(properties->value, "country", &(*respAddr)->country) || something;
242 something = __get_string(properties->value, "country_a", &(*respAddr)->country_a) || something;
243 something = __get_string(properties->value, "postalcode", &(*respAddr)->postalcode) || something;
244 //if everything was null thats pretty much unusable
245 //TODO: these are pretty weak criteria..
249 //forget the rest, we have one with something in it
260 /**************** PLACE SEARCH ********************/
262 static void __parse_place_response(char *response, int size, GList **placeList)
264 MAP_DEBUG("Inside __parse_place_response.");
266 if (!response || !placeList) return;
270 //crack open that json
271 rapidjson::Document document;
272 document.Parse(std::string(response, size).c_str());
273 rapidjson::Value::ConstMemberIterator features = document.FindMember("features");
274 if(features != document.MemberEnd() && features->value.IsArray()) {
276 for (rapidjson::Value::ConstValueIterator f = features->value.Begin(); f != features->value.End(); ++f) {
277 //it has to have geometry
278 rapidjson::Value::ConstMemberIterator geom = f->FindMember("geometry");
279 if(geom != f->MemberEnd()) {
281 rapidjson::Value::ConstMemberIterator type = geom->value.FindMember("type");
282 if(type == geom->value.MemberEnd() || strcmp(type->value.GetString(), "Point"))
285 rapidjson::Value::ConstMemberIterator coords = geom->value.FindMember("coordinates");
286 if(coords == geom->value.MemberEnd() || !coords->value.IsArray() || coords->value.Size() != 2)
290 coordinate.longitude = coords->value[0].GetDouble();
291 coordinate.latitude = coords->value[1].GetDouble();
293 //get out the address information
294 rapidjson::Value::ConstMemberIterator properties = f->FindMember("properties");
295 if(properties != f->MemberEnd()) {
296 //fill this out as we go
297 mapzen_place_resp_s *respPlaces = (mapzen_place_resp_s *)g_malloc0(sizeof(mapzen_place_resp_s));
299 respPlaces->place_id = NULL;
300 respPlaces->categories = NULL;
301 respPlaces->display_name = NULL;
302 respPlaces->address = NULL;
304 bool something = false;
305 something = __get_string(properties->value, "gid", &respPlaces->place_id) || something;
306 something = __get_string(properties->value, "name", &respPlaces->display_name) || something;
308 respPlaces->address = (mapzen_address_resp_s *)g_malloc(sizeof(mapzen_address_resp_s));
310 something = __get_string(properties->value, "housenumber", &respPlaces->address->housenumber) || something;
311 something = __get_string(properties->value, "street", &respPlaces->address->street) || something;
312 something = __get_string(properties->value, "neighbourhood", &respPlaces->address->neighbourhood) || something;
313 something = __get_string(properties->value, "county", &respPlaces->address->county) || something;
314 something = __get_string(properties->value, "region", &respPlaces->address->region) || something;
315 something = __get_string(properties->value, "country", &respPlaces->address->country) || something;
316 something = __get_string(properties->value, "country_a", &respPlaces->address->country_a) || something;
317 something = __get_string(properties->value, "locality", &respPlaces->address->localadmin) || something;
318 something = __get_string(properties->value, "postalcode", &respPlaces->address->postalcode) || something;
320 respPlaces->coordinates = coordinate;
322 respPlaces->categories = NULL;
323 rapidjson::Value::ConstMemberIterator categories = properties->value.FindMember("category");
324 if (categories != geom->value.MemberEnd() && categories->value.IsArray()) {
325 for (rapidjson::SizeType i = 0; i < categories->value.Size(); i++) {
326 if (respPlaces->categories == NULL)
327 respPlaces->categories = g_list_append(respPlaces->categories, g_strdup(categories->value[i].GetString()));
329 respPlaces->categories = g_list_insert_before(respPlaces->categories, NULL, g_strdup(categories->value[i].GetString()));
337 if (*placeList == NULL)
338 *placeList = g_list_append(*placeList, respPlaces);
340 *placeList = g_list_insert_before(*placeList, NULL, respPlaces);
348 /********************* ROUTE RESPONSE ***********************/
350 static void __parse_route_response(char *response, int size, int *status, mapzen_route_resp_s **routeResp)
352 if (!response || !status || !routeResp) return;
357 void post_curl_response(char *response, int size, mapzen_resp_type type, void *user_data)
359 if (!response) return;
362 MAP_DEBUG("Response data is NULL");
366 MAP_DEBUG("Response received from Curl. [Size=%d]", size);
368 case RESP_TYPE_GEOCODE:
370 MAP_DEBUG("Inside Geocode JSON Parsing..");
372 MapzenGeocodeQueryData *queryData = (MapzenGeocodeQueryData *)user_data;
373 MapzenGeocodeResponseData *responseData = (MapzenGeocodeResponseData *)g_malloc(sizeof(MapzenGeocodeResponseData));
376 responseData->requestId = queryData->requestId;
377 responseData->geocode_cb = queryData->geocode_cb;
378 responseData->user_data = queryData->user_data;
380 if (response && (size > 0)) {
381 GList *coordsList = NULL;
382 __parse_geocode_response(response, size, &status, &coordsList);
384 if (coordsList != NULL) {
385 /* Put the response in queue */
386 responseData->error = __convert_status(status);
387 responseData->coords = coordsList;
389 /* Response parsing failure */
390 responseData->error = __convert_status(status);
391 responseData->coords = NULL;
394 responseData->error = __convert_status(status);
395 responseData->coords = NULL;
398 mapzen_push_to_queue(type, (gpointer)responseData);
407 case RESP_TYPE_REVGEOCODE:
409 MAP_DEBUG("Inside Rev Geocode JSON Parsing..");
411 MapzenRevGeocodeQueryData *queryData = (MapzenRevGeocodeQueryData *)user_data;
412 MapzenRevGeocodeResponseData *responseData = (MapzenRevGeocodeResponseData *)g_malloc(sizeof(MapzenRevGeocodeResponseData));
415 responseData->requestId = queryData->requestId;
416 responseData->reverse_geocode_cb = queryData->reverse_geocode_cb;
417 responseData->user_data = queryData->user_data;
419 if (response && (size > 0)) {
420 /* Coords Result GList */
421 mapzen_address_resp_s *addrResponse = NULL;
423 MAP_DEBUG("Rev Geocode :- Parsing json Response");
424 __parse_revgeocode_response(response, size, &status, &addrResponse);
426 if (addrResponse != NULL) {
427 /* Put the response in queue */
428 responseData->error = __convert_status(status);
429 responseData->addressDetails = addrResponse;
431 /* REPSONSE PARSING FAILURE */
432 MAP_DEBUG("addr Response is NULL");
433 responseData->error = __convert_status(status);
434 responseData->addressDetails = NULL;
437 MAP_DEBUG("JSON Response is NULL..");
438 responseData->error = __convert_status(status);
439 responseData->addressDetails = NULL;
441 mapzen_push_to_queue(type, (gpointer)responseData);
450 case RESP_TYPE_PLACES:
452 MAP_DEBUG("Inside Places JSON Parsing..");
453 MapzenPlaceQueryData *queryData = (MapzenPlaceQueryData *)user_data;
454 MapzenPlaceResponseData *responseData = (MapzenPlaceResponseData *)g_malloc(sizeof(MapzenPlaceResponseData));
457 responseData->requestId = queryData->requestId;
458 responseData->place_search_cb = queryData->place_search_cb;
459 responseData->user_data = queryData->user_data;
461 if (response && (size > 0)) {
462 /* Coords Result GList */
463 GList *placeList = NULL;
465 MAP_DEBUG("Search Places :- Parsing Json Response");
466 __parse_place_response(response, size, &placeList);
468 if (placeList != NULL) {
469 /* Put the response in queue */
470 responseData->error = MAPZEN_ERROR_NONE;
471 responseData->places = placeList;
473 /* REPSONSE PARSING FAILURE */
474 MAP_DEBUG("addr Response is NULL");
475 responseData->error = MAPZEN_ERROR_UNKNOWN;
476 responseData->places = NULL;
479 responseData->error = MAPZEN_ERROR_UNKNOWN;
480 responseData->places = NULL;
483 mapzen_push_to_queue(type, (gpointer)responseData);
493 case RESP_TYPE_PLACES_DETAILS:
495 MAP_DEBUG("Inside Places Details JSON Parsing..");
496 MapzenPlaceDetailsQueryData *queryData = (MapzenPlaceDetailsQueryData *)user_data;
497 MapzenPlaceDetailsResponseData *responseData = (MapzenPlaceDetailsResponseData *)g_malloc(sizeof(MapzenPlaceDetailsResponseData));
500 responseData->requestId = queryData->requestId;
501 responseData->get_place_details_cb = queryData->get_place_details_cb;
502 responseData->user_data = queryData->user_data;
504 if (response && (size > 0)) {
505 /* Coords Result GList */
506 GList *placeList = NULL;
508 MAP_DEBUG("Search Places Details :- Parsing Json Response");
509 __parse_place_response(response, size, &placeList);
511 if (placeList != NULL) {
512 /* Put the response in queue */
513 responseData->error = MAPZEN_ERROR_NONE;
514 responseData->places = placeList;
516 /* REPSONSE PARSING FAILURE */
517 MAP_DEBUG("addr Response is NULL");
518 responseData->error = MAPZEN_ERROR_UNKNOWN;
519 responseData->places = NULL;
522 responseData->error = MAPZEN_ERROR_UNKNOWN;
523 responseData->places = NULL;
526 mapzen_push_to_queue(type, (gpointer)responseData);
536 case RESP_TYPE_PLACES_LIST:
538 MAP_DEBUG("Inside Places List JSON Parsing..");
539 MapzenPlaceListQueryData *queryData = (MapzenPlaceListQueryData *)user_data;
540 MapzenPlaceListResponseData *responseData = (MapzenPlaceListResponseData *)g_malloc(sizeof(MapzenPlaceListResponseData));
543 responseData->requestId = queryData->requestId;
544 responseData->place_list_search_cb = queryData->place_list_search_cb;
545 responseData->user_data = queryData->user_data;
547 if (response && (size > 0)) {
548 /* Coords Result GList */
549 GList *placeList = NULL;
551 MAP_DEBUG("Search Places :- Parsing Json Response");
552 __parse_place_response(response, size, &placeList);
554 if (placeList != NULL) {
555 /* Put the response in queue */
556 responseData->error = MAPZEN_ERROR_NONE;
557 responseData->places = placeList;
559 /* REPSONSE PARSING FAILURE */
560 MAP_DEBUG("addr Response is NULL");
561 responseData->error = MAPZEN_ERROR_UNKNOWN;
562 responseData->places = NULL;
565 responseData->error = MAPZEN_ERROR_UNKNOWN;
566 responseData->places = NULL;
569 mapzen_push_to_queue(type, (gpointer)responseData);
579 case RESP_TYPE_ROUTE:
581 MAP_DEBUG("Inside Route JSON Parsing..");
583 MapzenRouteQueryData *queryData = (MapzenRouteQueryData *)user_data;
584 __route_unit = queryData->unit;
585 __maneuver_index = 0;
586 __destination_point = queryData->destination;
588 MapzenRouteResponseData *responseData = (MapzenRouteResponseData *)g_malloc(sizeof(MapzenRouteResponseData));
591 responseData->requestId = queryData->requestId;
592 responseData->route_cb = queryData->route_cb;
593 responseData->user_data = queryData->user_data;
595 if (response && (size > 0)) {
596 /* Coords Result GList */
597 mapzen_route_resp_s *routeResponse = NULL;
599 MAP_DEBUG("Route :- Parsing Response");
600 __parse_route_response(response, size, &status, &routeResponse);
602 if (routeResponse != NULL) {
603 /* Put the response in queue */
604 responseData->error = __convert_status(status);
605 responseData->routeResponse = routeResponse;
607 /* REPSONSE PARSING FAILURE */
608 MAP_DEBUG("route Response is NULL");
609 responseData->error = __convert_status(status);
610 responseData->routeResponse = NULL;
613 responseData->error = __convert_status(status);
614 responseData->routeResponse = NULL;
617 mapzen_push_to_queue(type, (gpointer)responseData);
629 MAP_DEBUG("Inside default JSON Parsing..");