GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/webapis_geomaps.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 81 146 55.5%
Functions: 6 8 75.0%
Branches: 12 52 23.1%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/webapis.h"
30 #include "gate/results.h"
31 #include "gate/geopositions.h"
32 #include "gate/encode/json.h"
33
34 #define GATE_WEBAPIS_GEOMAPS_REQUEST_NAME GATE_STRUCT_WEBAPIS_NAME("geomaps_request")
35 static gate_struct_item_t const geomaps_request_members[] =
36 {
37 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_STRING, provider, "provider", GATE_STRUCT_FLAG_DEFAULT),
38 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_UI32, image_width, "image_width", GATE_STRUCT_FLAG_DEFAULT),
39 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_UI32, image_height, "image_height", GATE_STRUCT_FLAG_DEFAULT),
40 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_R32, center_latitude, "center_latitude", GATE_STRUCT_FLAG_DEFAULT),
41 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_R32, center_longitude, "center_longitude", GATE_STRUCT_FLAG_DEFAULT),
42 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_R32, radius_meters, "radius_meters", GATE_STRUCT_FLAG_DEFAULT),
43 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_I16, image_type, "image_type", GATE_STRUCT_FLAG_DEFAULT),
44 };
45 static gate_struct_descriptor_t const geomaps_request_descriptor =
46 GATE_STRUCT_DESCRIPTOR(gate_tech_webapi_geomaps_request_t, GATE_WEBAPIS_GEOMAPS_REQUEST_NAME, geomaps_request_members);
47
48 1 void gate_tech_webapi_geomaps_request_init(gate_tech_webapi_geomaps_request_t* request)
49 {
50 1 gate_mem_clear(request, sizeof(gate_tech_webapi_geomaps_request_t));
51 1 request->struct_base.struct_descriptor = &geomaps_request_descriptor;
52 1 }
53
54
55
56 #define GATE_WEBAPIS_GEOMAPS_RESPONSE_NAME GATE_STRUCT_WEBAPIS_NAME("geomaps_response")
57 static gate_struct_item_t const geomaps_response_members[] =
58 {
59 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_UI32, image_width, "image_width", GATE_STRUCT_FLAG_DEFAULT),
60 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_UI32, image_height, "image_height", GATE_STRUCT_FLAG_DEFAULT),
61 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, center_latitude, "center_latitude", GATE_STRUCT_FLAG_DEFAULT),
62 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, center_longitude, "center_longitude", GATE_STRUCT_FLAG_DEFAULT),
63 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, min_latitude, "min_latitude", GATE_STRUCT_FLAG_DEFAULT),
64 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, min_longitude, "min_longitude", GATE_STRUCT_FLAG_DEFAULT),
65 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, max_latitude, "max_latitude", GATE_STRUCT_FLAG_DEFAULT),
66 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, max_longitude, "max_longitude", GATE_STRUCT_FLAG_DEFAULT),
67 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_I16, image_type, "image_type", GATE_STRUCT_FLAG_DEFAULT),
68 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_I16, image_format, "image_format", GATE_STRUCT_FLAG_DEFAULT),
69 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_I16, image_data, "image_data", GATE_STRUCT_FLAG_DEFAULT),
70 };
71 static gate_struct_descriptor_t const geomaps_response_descriptor =
72 GATE_STRUCT_DESCRIPTOR(gate_tech_webapi_geomaps_response_t, GATE_WEBAPIS_GEOMAPS_RESPONSE_NAME, geomaps_response_members);
73
74 1 void gate_tech_webapi_geomaps_response_init(gate_tech_webapi_geomaps_response_t* response)
75 {
76 1 gate_mem_clear(response, sizeof(gate_tech_webapi_geomaps_response_t));
77 1 response->struct_base.struct_descriptor = &geomaps_response_descriptor;
78 1 }
79
80 static gate_real32_t extract_real_item(gate_property_t const* prop_arr, gate_size_t ndx)
81 {
82 gate_property_t const* prop = gate_property_array_get(prop_arr, ndx);
83 gate_string_t str = GATE_STRING_INIT_EMPTY;
84 gate_real64_t r = 0.0;
85 gate_property_typeid_t proptype;
86 gate_result_t result;
87 if (NULL != prop)
88 {
89 proptype = gate_property_get_type(prop);
90 if (proptype == GATE_PROPERTY_TYPE_STRING)
91 {
92 result = gate_property_get_string(prop, &str);
93 if (GATE_SUCCEEDED(result))
94 {
95 gate_string_parse_real(&str, &r);
96 }
97 }
98 else
99 {
100 gate_property_get_real(prop, &r);
101 }
102 }
103 gate_string_release(&str);
104 return (gate_real32_t)r;
105 }
106
107 static gate_uint32_t extract_str_ui32_member(gate_property_t const* prop_arr, gate_string_t const* name)
108 {
109 gate_property_t const* prop = gate_property_member_get(prop_arr, name);
110 gate_string_t str = GATE_STRING_INIT_EMPTY;
111 gate_int64_t i = 0;
112 gate_result_t result;
113 if (NULL != prop)
114 {
115 result = gate_property_get_string(prop, &str);
116 if (GATE_SUCCEEDED(result))
117 {
118 gate_string_parse_int(&str, &i);
119 }
120 }
121 gate_string_release(&str);
122 return (gate_uint32_t)i;
123 }
124
125 #define BREAK_IF_NULL(varname) if(NULL == varname) { break; }
126
127 1 static gate_result_t extract_properties(gate_property_t const* prop, gate_tech_webapi_geomaps_response_t* response)
128 {
129 static gate_string_t const propname_resourceSets = GATE_STRING_INIT_STATIC("resourceSets");
130 static gate_string_t const propname_resources = GATE_STRING_INIT_STATIC("resources");
131 static gate_string_t const propname_bbox = GATE_STRING_INIT_STATIC("bbox");
132 static gate_string_t const propname_mapCenter = GATE_STRING_INIT_STATIC("mapCenter");
133 static gate_string_t const propname_coordinates = GATE_STRING_INIT_STATIC("coordinates");
134 static gate_string_t const propname_imageWidth = GATE_STRING_INIT_STATIC("imageWidth");
135 static gate_string_t const propname_imageHeight = GATE_STRING_INIT_STATIC("imageHeight");
136 1 gate_property_t const* resource_sets = NULL;
137 1 gate_property_t const* resource_set0 = NULL;
138 1 gate_property_t const* resources = NULL;
139 1 gate_property_t const* resource = NULL;
140 1 gate_property_t const* bbox = NULL;
141 1 gate_property_t const* center = NULL;
142 1 gate_property_t const* coords = NULL;
143 1 gate_result_t ret = GATE_RESULT_INVALIDCONTENT;
144
145 do
146 {
147 1 resource_sets = gate_property_member_get(prop, &propname_resourceSets);
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BREAK_IF_NULL(resource_sets);
149 1 resource_set0 = gate_property_array_get(resource_sets, 0);
150
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 BREAK_IF_NULL(resource_set0);
151 resources = gate_property_member_get(resource_set0, &propname_resources);
152 BREAK_IF_NULL(resources);
153 resource = gate_property_array_get(resources, 0);
154 BREAK_IF_NULL(resource);
155 bbox = gate_property_member_get(resource, &propname_bbox);
156 BREAK_IF_NULL(bbox);
157 center = gate_property_member_get(resource, &propname_mapCenter);
158 BREAK_IF_NULL(center);
159 coords = gate_property_member_get(center, &propname_coordinates);
160 BREAK_IF_NULL(coords);
161
162 response->image_width = extract_str_ui32_member(resource, &propname_imageWidth);
163 response->image_height = extract_str_ui32_member(resource, &propname_imageHeight);
164
165 response->min_latitude = extract_real_item(bbox, 0);
166 response->min_longitude = extract_real_item(bbox, 1);
167 response->max_latitude = extract_real_item(bbox, 2);
168 response->max_longitude = extract_real_item(bbox, 3);
169
170 response->center_latitude = extract_real_item(coords, 0);
171 response->center_longitude = extract_real_item(coords, 1);
172 ret = GATE_RESULT_OK;
173
174 } while (0);
175
176 1 return ret;
177
178 }
179
180 1 static void create_bing_query(gate_strbuilder_t* query_builder,
181 gate_real32_t minlat, gate_real32_t minlong, gate_real32_t maxlat, gate_real32_t maxlong,
182 gate_uint32_t width, gate_uint32_t height, gate_bool_t as_metadata, gate_bool_t pngimage, gate_string_t const* apikey)
183 {
184 1 gate_strbuilder_append_cstr(query_builder, "?mapArea=");
185 1 gate_strbuilder_append_real(query_builder, minlat, 0, 6, 0);
186 1 gate_strbuilder_append_cstr(query_builder, ",");
187 1 gate_strbuilder_append_real(query_builder, minlong, 0, 6, 0);
188 1 gate_strbuilder_append_cstr(query_builder, ",");
189 1 gate_strbuilder_append_real(query_builder, maxlat, 0, 6, 0);
190 1 gate_strbuilder_append_cstr(query_builder, ",");
191 1 gate_strbuilder_append_real(query_builder, maxlong, 0, 6, 0);
192 1 gate_strbuilder_append_cstr(query_builder, "&mapSize=");
193 1 gate_strbuilder_append_int32(query_builder, width);
194 1 gate_strbuilder_append_cstr(query_builder, ",");
195 1 gate_strbuilder_append_int32(query_builder, height);
196
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (as_metadata)
197 {
198 1 gate_strbuilder_append_cstr(query_builder, "&mapMetadata=1");
199 }
200 else
201 {
202 if (pngimage)
203 {
204 gate_strbuilder_append_cstr(query_builder, "&format=png");
205 }
206 }
207 1 gate_strbuilder_append_cstr(query_builder, "&key=");
208 1 gate_strbuilder_append_string(query_builder, apikey);
209 1 }
210
211 #define BING_MAP_BASE_URL "http://dev.virtualearth.net/REST/V1/Imagery/Map/"
212 static gate_string_t const bing_road_url = GATE_STRING_INIT_STATIC(BING_MAP_BASE_URL "road");
213 static gate_string_t const bing_aerial_url = GATE_STRING_INIT_STATIC(BING_MAP_BASE_URL "aerial");
214 static gate_string_t const bing_aerialwithlabels_url = GATE_STRING_INIT_STATIC(BING_MAP_BASE_URL "aerialwithlabels");
215 static gate_string_t const bing_apikey = GATE_STRING_INIT_STATIC("MvLhnePhoOakkLRadvjE~DqbqTh8gUgq5MFown4hTHQ~AvoZiHcNoG8i8wJxC5vjA36KWU0qJiwqAuL3ZWy9vYKcGuQvDrPFiGqwAX3BUqC6");
216
217
218 1 static gate_result_t bing_load_map_info(gate_tech_webapi_context_t* context,
219 gate_tech_webapi_geomaps_request_t* request,
220 gate_tech_webapi_geomaps_response_t* response)
221 {
222 1 const gate_real32_t mplat = (gate_real32_t)gate_geopos_meters_per_latitude(request->center_latitude);
223 1 const gate_real32_t mplon = (gate_real32_t)gate_geopos_meters_per_longitude(request->center_latitude);
224 1 const gate_real32_t minlat = request->center_latitude - request->radius_meters / mplat;
225 1 const gate_real32_t minlong = request->center_longitude - request->radius_meters / mplon;
226 1 const gate_real32_t maxlat = request->center_latitude + request->radius_meters / mplat;
227 1 const gate_real32_t maxlong = request->center_longitude + request->radius_meters / mplon;
228
229 1 gate_result_t ret = GATE_RESULT_FAILED;
230 1 gate_uri_t url = GATE_INIT_EMPTY;
231 1 gate_uint32_t http_status = 0;
232 1 gate_memstream_t* ptr_content = NULL;
233 1 gate_property_t jsonprops = GATE_INIT_EMPTY;
234 1 gate_json_result_t jsonresult = GATE_INIT_EMPTY;
235 1 gate_size_t data_size = 0;
236 1 char const* ptr_data = NULL;
237 char query_buffer[1024];
238 1 gate_strbuilder_t query_builder = GATE_INIT_EMPTY;
239 1 gate_bool_t png_image = false;
240
241 do
242 {
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (request->image_type == GATE_TECH_GEOMAPS_TYPE_ROADMAP)
244 {
245 ret = gate_uri_parse(&url, &bing_road_url);
246 png_image = true;
247 }
248
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (request->image_type == GATE_TECH_GEOMAPS_TYPE_SATELLITEMAP)
249 {
250 1 ret = gate_uri_parse(&url, &bing_aerial_url);
251 }
252 else if (request->image_type == (GATE_TECH_GEOMAPS_TYPE_ROADMAP | GATE_TECH_GEOMAPS_TYPE_SATELLITEMAP))
253 {
254 ret = gate_uri_parse(&url, &bing_aerialwithlabels_url);
255 }
256 else
257 {
258 ret = GATE_RESULT_INVALIDARG;
259 }
260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
261
262 1 gate_strbuilder_create_static(&query_builder, query_buffer, sizeof(query_buffer), 0);
263 1 create_bing_query(&query_builder,
264 minlat, minlong, maxlat, maxlong,
265 request->image_width, request->image_height, true, png_image, &bing_apikey);
266 1 gate_strbuilder_to_string(&query_builder, &url.query);
267
268 1 ptr_content = gate_memstream_create(256);
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_content)
270 {
271 ret = GATE_RESULT_OUTOFMEMORY;
272 break;
273 }
274
275 1 ret = gate_tech_webapi_get(context, &url, NULL, &http_status, (gate_stream_t*)ptr_content, NULL);
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
277
278 1 ret = gate_json_parse((gate_stream_t*)ptr_content, &jsonprops, &jsonresult);
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!jsonresult.succeeded)
281 {
282 ret = GATE_RESULT_INVALIDDATA;
283 break;
284 }
285 1 ret = extract_properties(&jsonprops, response);
286
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 GATE_BREAK_IF_FAILED(ret);
287
288 /* we have required meta data, now retrieve image content */
289
290 gate_strbuilder_release(&query_builder);
291 gate_strbuilder_create_static(&query_builder, query_buffer, sizeof(query_buffer), 0);
292 create_bing_query(&query_builder,
293 response->min_latitude, response->min_longitude, response->max_latitude, response->max_longitude,
294 response->image_width, response->image_height, false, png_image, &bing_apikey);
295 /* release old query, emplace new one */
296 gate_string_release(&url.query);
297 gate_strbuilder_to_string(&query_builder, &url.query);
298
299 gate_memstream_discard(ptr_content, gate_memstream_size(ptr_content));
300
301 ret = gate_tech_webapi_get(context, &url, NULL, &http_status, (gate_stream_t*)ptr_content, NULL);
302 GATE_BREAK_IF_FAILED(ret);
303
304 data_size = gate_memstream_size(ptr_content);
305 ptr_data = gate_memstream_get_data(ptr_content);
306 gate_blob_create(&response->image_data, ptr_data, data_size);
307 response->image_format = png_image ? GATE_TECH_GEOMAPS_IMAGE_PNG : GATE_TECH_GEOMAPS_IMAGE_JPEG;
308
309 } while (0);
310
311 1 gate_property_destroy(&jsonprops);
312
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_content != NULL)
313 {
314 1 gate_object_release(ptr_content);
315 }
316 1 gate_uri_destroy(&url);
317 1 gate_strbuilder_release(&query_builder);
318 1 return ret;
319 }
320
321 1 gate_result_t gate_tech_webapi_geomaps(gate_tech_webapi_context_t* context,
322 gate_tech_webapi_geomaps_request_t* request,
323 gate_tech_webapi_geomaps_response_t* response)
324 {
325 1 return bing_load_map_info(context, request, response);
326 }
327