GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/webapis_geomaps.c
Date: 2026-09-21 06:26:40
Exec Total Coverage
Lines: 105 116 90.5%
Functions: 14 14 100.0%
Branches: 16 28 57.1%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2026, 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 #include "gate/utilities.h"
34 #include "gate/mathematics.h"
35
36 #define GATE_WEBAPIS_GEOMAPS_REQUEST_NAME GATE_STRUCT_WEBAPIS_NAME("geomaps_request")
37 static gate_struct_item_t const geomaps_request_members[] =
38 {
39 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_STRING, provider, "provider", GATE_STRUCT_FLAG_DEFAULT),
40 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_UI32, image_width, "image_width", GATE_STRUCT_FLAG_DEFAULT),
41 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_UI32, image_height, "image_height", GATE_STRUCT_FLAG_DEFAULT),
42 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_R32, center_latitude, "center_latitude", GATE_STRUCT_FLAG_DEFAULT),
43 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_R32, center_longitude, "center_longitude", GATE_STRUCT_FLAG_DEFAULT),
44 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_R32, radius_meters, "radius_meters", GATE_STRUCT_FLAG_DEFAULT),
45 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_request_t, GATE_TYPE_I16, image_type, "image_type", GATE_STRUCT_FLAG_DEFAULT),
46 };
47
48 1 void gate_tech_webapi_geomaps_request_init(gate_tech_webapi_geomaps_request_t* request)
49 {
50 static gate_struct_descriptor_t const geomaps_request_descriptor =
51 GATE_STRUCT_DESCRIPTOR(gate_tech_webapi_geomaps_request_t, GATE_WEBAPIS_GEOMAPS_REQUEST_NAME, geomaps_request_members);
52
53 1 gate_mem_clear(request, sizeof(gate_tech_webapi_geomaps_request_t));
54 1 request->struct_base.struct_descriptor = &geomaps_request_descriptor;
55 1 }
56
57
58
59 #define GATE_WEBAPIS_GEOMAPS_RESPONSE_NAME GATE_STRUCT_WEBAPIS_NAME("geomaps_response")
60 static gate_struct_item_t const geomaps_response_members[] =
61 {
62 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_UI32, image_width, "image_width", GATE_STRUCT_FLAG_DEFAULT),
63 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_UI32, image_height, "image_height", GATE_STRUCT_FLAG_DEFAULT),
64 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, center_latitude, "center_latitude", GATE_STRUCT_FLAG_DEFAULT),
65 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, center_longitude, "center_longitude", GATE_STRUCT_FLAG_DEFAULT),
66 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, min_latitude, "min_latitude", GATE_STRUCT_FLAG_DEFAULT),
67 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, min_longitude, "min_longitude", GATE_STRUCT_FLAG_DEFAULT),
68 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, max_latitude, "max_latitude", GATE_STRUCT_FLAG_DEFAULT),
69 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, max_longitude, "max_longitude", GATE_STRUCT_FLAG_DEFAULT),
70 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, width_meters, "width_meters", GATE_STRUCT_FLAG_DEFAULT),
71 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_R32, height_meters, "height_meters", GATE_STRUCT_FLAG_DEFAULT),
72 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_I16, image_type, "image_type", GATE_STRUCT_FLAG_DEFAULT),
73 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_I16, image_format, "image_format", GATE_STRUCT_FLAG_DEFAULT),
74 GATE_STRUCT_ITEM_EX(gate_tech_webapi_geomaps_response_t, GATE_TYPE_I16, image_data, "image_data", GATE_STRUCT_FLAG_DEFAULT),
75 };
76
77 3 void gate_tech_webapi_geomaps_response_init(gate_tech_webapi_geomaps_response_t* response)
78 {
79 static gate_struct_descriptor_t const geomaps_response_descriptor =
80 GATE_STRUCT_DESCRIPTOR(gate_tech_webapi_geomaps_response_t, GATE_WEBAPIS_GEOMAPS_RESPONSE_NAME, geomaps_response_members);
81 3 gate_mem_clear(response, sizeof(gate_tech_webapi_geomaps_response_t));
82 3 response->struct_base.struct_descriptor = &geomaps_response_descriptor;
83 3 }
84
85 /*
86
87 // old implementation for BingMaps, service shutdown in 2025 //
88
89 static gate_real32_t extract_real_item(gate_property_t const* prop_arr, gate_size_t ndx)
90 {
91 gate_property_t const* prop = gate_property_array_get(prop_arr, ndx);
92 gate_string_t str = GATE_STRING_INIT_EMPTY;
93 gate_real64_t r = 0.0;
94 gate_property_typeid_t proptype;
95 gate_result_t result;
96 if (NULL != prop)
97 {
98 proptype = gate_property_get_type(prop);
99 if (proptype == GATE_PROPERTY_TYPE_STRING)
100 {
101 result = gate_property_get_string(prop, &str);
102 if (GATE_SUCCEEDED(result))
103 {
104 gate_string_parse_real(&str, &r);
105 }
106 }
107 else
108 {
109 gate_property_get_real(prop, &r);
110 }
111 }
112 gate_string_release(&str);
113 return (gate_real32_t)r;
114 }
115
116 static gate_uint32_t extract_str_ui32_member(gate_property_t const* prop_arr, gate_string_t const* name)
117 {
118 gate_property_t const* prop = gate_property_member_get(prop_arr, name);
119 gate_string_t str = GATE_STRING_INIT_EMPTY;
120 gate_int64_t i = 0;
121 gate_result_t result;
122 if (NULL != prop)
123 {
124 result = gate_property_get_string(prop, &str);
125 if (GATE_SUCCEEDED(result))
126 {
127 gate_string_parse_int(&str, &i);
128 }
129 }
130 gate_string_release(&str);
131 return (gate_uint32_t)i;
132 }
133
134 #define BREAK_IF_NULL(varname) if(NULL == varname) { break; }
135
136 static gate_result_t extract_properties(gate_property_t const* prop, gate_tech_webapi_geomaps_response_t* response)
137 {
138 static gate_string_t const propname_resourceSets = GATE_STRING_INIT_STATIC("resourceSets");
139 static gate_string_t const propname_resources = GATE_STRING_INIT_STATIC("resources");
140 static gate_string_t const propname_bbox = GATE_STRING_INIT_STATIC("bbox");
141 static gate_string_t const propname_mapCenter = GATE_STRING_INIT_STATIC("mapCenter");
142 static gate_string_t const propname_coordinates = GATE_STRING_INIT_STATIC("coordinates");
143 static gate_string_t const propname_imageWidth = GATE_STRING_INIT_STATIC("imageWidth");
144 static gate_string_t const propname_imageHeight = GATE_STRING_INIT_STATIC("imageHeight");
145 gate_property_t const* resource_sets = NULL;
146 gate_property_t const* resource_set0 = NULL;
147 gate_property_t const* resources = NULL;
148 gate_property_t const* resource = NULL;
149 gate_property_t const* bbox = NULL;
150 gate_property_t const* center = NULL;
151 gate_property_t const* coords = NULL;
152 gate_result_t ret = GATE_RESULT_INVALIDCONTENT;
153
154 do
155 {
156 resource_sets = gate_property_member_get(prop, &propname_resourceSets);
157 BREAK_IF_NULL(resource_sets);
158 resource_set0 = gate_property_array_get(resource_sets, 0);
159 BREAK_IF_NULL(resource_set0);
160 resources = gate_property_member_get(resource_set0, &propname_resources);
161 BREAK_IF_NULL(resources);
162 resource = gate_property_array_get(resources, 0);
163 BREAK_IF_NULL(resource);
164 bbox = gate_property_member_get(resource, &propname_bbox);
165 BREAK_IF_NULL(bbox);
166 center = gate_property_member_get(resource, &propname_mapCenter);
167 BREAK_IF_NULL(center);
168 coords = gate_property_member_get(center, &propname_coordinates);
169 BREAK_IF_NULL(coords);
170
171 response->image_width = extract_str_ui32_member(resource, &propname_imageWidth);
172 response->image_height = extract_str_ui32_member(resource, &propname_imageHeight);
173
174 response->min_latitude = extract_real_item(bbox, 0);
175 response->min_longitude = extract_real_item(bbox, 1);
176 response->max_latitude = extract_real_item(bbox, 2);
177 response->max_longitude = extract_real_item(bbox, 3);
178
179 response->center_latitude = extract_real_item(coords, 0);
180 response->center_longitude = extract_real_item(coords, 1);
181 ret = GATE_RESULT_OK;
182
183 } while (0);
184
185 return ret;
186
187 }
188
189 static void create_bing_query(gate_strbuilder_t* query_builder,
190 gate_real32_t minlat, gate_real32_t minlong, gate_real32_t maxlat, gate_real32_t maxlong,
191 gate_uint32_t width, gate_uint32_t height, gate_bool_t as_metadata, gate_bool_t pngimage, gate_string_t const* apikey)
192 {
193 gate_strbuilder_append_cstr(query_builder, "?mapArea=");
194 gate_strbuilder_append_real(query_builder, minlat, 0, 6, 0);
195 gate_strbuilder_append_cstr(query_builder, ",");
196 gate_strbuilder_append_real(query_builder, minlong, 0, 6, 0);
197 gate_strbuilder_append_cstr(query_builder, ",");
198 gate_strbuilder_append_real(query_builder, maxlat, 0, 6, 0);
199 gate_strbuilder_append_cstr(query_builder, ",");
200 gate_strbuilder_append_real(query_builder, maxlong, 0, 6, 0);
201 gate_strbuilder_append_cstr(query_builder, "&mapSize=");
202 gate_strbuilder_append_int32(query_builder, width);
203 gate_strbuilder_append_cstr(query_builder, ",");
204 gate_strbuilder_append_int32(query_builder, height);
205 if (as_metadata)
206 {
207 gate_strbuilder_append_cstr(query_builder, "&mapMetadata=1");
208 }
209 else
210 {
211 if (pngimage)
212 {
213 gate_strbuilder_append_cstr(query_builder, "&format=png");
214 }
215 }
216 gate_strbuilder_append_cstr(query_builder, "&key=");
217 gate_strbuilder_append_string(query_builder, apikey);
218 }
219
220 #define BING_MAP_BASE_URL "http://dev.virtualearth.net/REST/V1/Imagery/Map/"
221 static gate_string_t const bing_road_url = GATE_STRING_INIT_STATIC(BING_MAP_BASE_URL "road");
222 static gate_string_t const bing_aerial_url = GATE_STRING_INIT_STATIC(BING_MAP_BASE_URL "aerial");
223 static gate_string_t const bing_aerialwithlabels_url = GATE_STRING_INIT_STATIC(BING_MAP_BASE_URL "aerialwithlabels");
224 static gate_string_t const bing_apikey = GATE_STRING_INIT_STATIC("MvLhnePhoOakkLRadvjE~DqbqTh8gUgq5MFown4hTHQ~AvoZiHcNoG8i8wJxC5vjA36KWU0qJiwqAuL3ZWy9vYKcGuQvDrPFiGqwAX3BUqC6");
225
226
227 static gate_result_t bing_load_map_info(gate_tech_webapi_context_t* context,
228 gate_tech_webapi_geomaps_request_t* request,
229 gate_tech_webapi_geomaps_response_t* response)
230 {
231 const gate_real32_t mplat = (gate_real32_t)gate_geopos_meters_per_latitude(request->center_latitude);
232 const gate_real32_t mplon = (gate_real32_t)gate_geopos_meters_per_longitude(request->center_latitude);
233 const gate_real32_t minlat = request->center_latitude - request->radius_meters / mplat;
234 const gate_real32_t minlong = request->center_longitude - request->radius_meters / mplon;
235 const gate_real32_t maxlat = request->center_latitude + request->radius_meters / mplat;
236 const gate_real32_t maxlong = request->center_longitude + request->radius_meters / mplon;
237
238 gate_result_t ret = GATE_RESULT_FAILED;
239 gate_uri_t url = GATE_INIT_EMPTY;
240 gate_uint32_t http_status = 0;
241 gate_memstream_t* ptr_content = NULL;
242 gate_property_t jsonprops = GATE_INIT_EMPTY;
243 gate_json_result_t jsonresult = GATE_INIT_EMPTY;
244 gate_size_t data_size = 0;
245 char const* ptr_data = NULL;
246 char query_buffer[1024];
247 gate_strbuilder_t query_builder = GATE_INIT_EMPTY;
248 gate_bool_t png_image = false;
249
250 do
251 {
252 if (request->image_type == GATE_TECH_GEOMAPS_TYPE_ROADMAP)
253 {
254 ret = gate_uri_parse(&url, &bing_road_url);
255 png_image = true;
256 }
257 else if (request->image_type == GATE_TECH_GEOMAPS_TYPE_SATELLITEMAP)
258 {
259 ret = gate_uri_parse(&url, &bing_aerial_url);
260 }
261 else if (request->image_type == (GATE_TECH_GEOMAPS_TYPE_ROADMAP | GATE_TECH_GEOMAPS_TYPE_SATELLITEMAP))
262 {
263 ret = gate_uri_parse(&url, &bing_aerialwithlabels_url);
264 }
265 else
266 {
267 ret = GATE_RESULT_INVALIDARG;
268 }
269 GATE_BREAK_IF_FAILED(ret);
270
271 gate_strbuilder_create_static(&query_builder, query_buffer, sizeof(query_buffer), 0);
272 create_bing_query(&query_builder,
273 minlat, minlong, maxlat, maxlong,
274 request->image_width, request->image_height, true, png_image, &bing_apikey);
275 gate_strbuilder_to_string(&query_builder, &url.query);
276
277 ptr_content = gate_memstream_create(256);
278 if (NULL == ptr_content)
279 {
280 ret = GATE_RESULT_OUTOFMEMORY;
281 break;
282 }
283
284 ret = gate_tech_webapi_get(context, &url, NULL, &http_status, (gate_stream_t*)ptr_content, NULL);
285 GATE_BREAK_IF_FAILED(ret);
286
287 ret = gate_json_parse((gate_stream_t*)ptr_content, &jsonprops, &jsonresult);
288 GATE_BREAK_IF_FAILED(ret);
289 if (!jsonresult.succeeded)
290 {
291 ret = GATE_RESULT_INVALIDDATA;
292 break;
293 }
294 ret = extract_properties(&jsonprops, response);
295 GATE_BREAK_IF_FAILED(ret);
296
297 // we have required meta data, now retrieve image content
298
299 gate_strbuilder_release(&query_builder);
300 gate_strbuilder_create_static(&query_builder, query_buffer, sizeof(query_buffer), 0);
301 create_bing_query(&query_builder,
302 response->min_latitude, response->min_longitude, response->max_latitude, response->max_longitude,
303 response->image_width, response->image_height, false, png_image, &bing_apikey);
304 // release old query, emplace new one
305 gate_string_release(&url.query);
306 gate_strbuilder_to_string(&query_builder, &url.query);
307
308 gate_memstream_discard(ptr_content, gate_memstream_size(ptr_content));
309
310 ret = gate_tech_webapi_get(context, &url, NULL, &http_status, (gate_stream_t*)ptr_content, NULL);
311 GATE_BREAK_IF_FAILED(ret);
312
313 data_size = gate_memstream_size(ptr_content);
314 ptr_data = gate_memstream_get_data(ptr_content);
315 gate_blob_create(&response->image_data, ptr_data, data_size);
316 response->image_format = png_image ? GATE_TECH_GEOMAPS_IMAGE_PNG : GATE_TECH_GEOMAPS_IMAGE_JPEG;
317
318 } while (0);
319
320 gate_property_destroy(&jsonprops);
321 if (ptr_content != NULL)
322 {
323 gate_object_release(ptr_content);
324 }
325 gate_uri_destroy(&url);
326 gate_strbuilder_release(&query_builder);
327 return ret;
328 }
329 */
330
331 3 static void azuremaps_read_api_key(gate_string_t* out_key)
332 {
333 static gate_string_t const subscription_key_value = GATE_STRING_INIT_STATIC("7LvLezLgOna4sU5bmBpqD5jAXdglfL8bcl3RaCjDBoVQAvfAgpvUJQQJ99BHAC5RqLJ2Rc9zAAAgAZMPDiUP");
334 3 *out_key = subscription_key_value;
335 3 }
336
337 static gate_real32_t const azure_R = 6378137.0f;
338 static gate_real32_t const azure_world_meters = 6378137.0f * GATE_MATH_CONST_PI * 2.0f;
339 static gate_real32_t const azure_tile_size = 256.0f;
340 static gate_real32_t const pi_4 = GATE_MATH_CONST_PI / 4.0f;
341 static gate_real32_t const pi_2 = GATE_MATH_CONST_PI / 2.0f;
342 static gate_real32_t const meters_per_pixel_zoom_0 = 156543.03392804097f;
343
344 3 static gate_real32_t get_mercator_meters_from_geopos_x(gate_real32_t longitude)
345 {
346 3 gate_real32_t const l = gate_math_deg2rad(longitude);
347 3 return azure_R * l;
348 }
349
350 3 static gate_real32_t get_mercator_meters_from_geopos_y(gate_real32_t latitude)
351 {
352 3 gate_real32_t const ph = gate_math_deg2rad(latitude);
353 3 return azure_R * gate_math_log(gate_math_tan(pi_4 + ph * 0.5f));
354 }
355
356 6 static gate_real32_t get_longitude_from_mercator_meters(gate_real32_t x)
357 {
358 6 return gate_math_rad2deg(x / azure_R);
359 }
360 6 static gate_real32_t get_latitude_from_mercator_meters(gate_real32_t y)
361 {
362 6 return gate_math_rad2deg(2.0 * gate_math_atan(gate_math_exp(y / azure_R)) - pi_2);
363 }
364
365 3 static gate_real32_t get_meters_per_pixel(gate_real32_t latitude, gate_uint16_t zoom_level)
366 {
367 3 const gate_real32_t zoom_factor = 1.0f / (gate_real32_t)(gate_math_pow_i64(2, zoom_level));
368 3 return meters_per_pixel_zoom_0 * gate_math_cos(gate_math_deg2rad(latitude)) * zoom_factor;
369 }
370
371 6 static gate_uint16_t azuremaps_calc_zoom_factor(gate_real32_t radius_meters, gate_uint16_t pixel_width, gate_uint16_t max_zoom)
372 {
373 6 gate_real32_t const view_meters = radius_meters * 2.0;
374 6 gate_real32_t const value = (azure_world_meters * (gate_real32_t)pixel_width) / (azure_tile_size * view_meters);
375 6 gate_real64_t const zoom = gate_math_floor(gate_math_log2(value));
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (zoom <= 1.0) return 1;
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (zoom >= (gate_real64_t)max_zoom) return max_zoom;
378 6 return (gate_uint16_t)zoom;
379 }
380
381 6 static gate_uint16_t azuremaps_get_max_zoom_factor(gate_enumint_t image_type)
382 {
383
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 switch(image_type)
384 {
385 2 case GATE_TECH_GEOMAPS_TYPE_SATELLITEMAP:
386 2 return 20;
387 4 case GATE_TECH_GEOMAPS_TYPE_ROADMAP:
388 default:
389 4 return 22;
390 }
391 }
392
393 3 static char const* azuremaps_get_image_type_string(gate_enumint_t image_type)
394 {
395
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 switch(image_type)
396 {
397 1 case GATE_TECH_GEOMAPS_TYPE_SATELLITEMAP:
398 1 return "microsoft.imagery";
399 2 case GATE_TECH_GEOMAPS_TYPE_ROADMAP:
400 default:
401 2 return "microsoft.base.road";
402 }
403 }
404
405 3 static gate_result_t azuremaps_fetch_static_image(
406 gate_tech_webapi_context_t* context,
407 gate_string_t const* api_key,
408 gate_real32_t longitude, gate_real32_t latitude,
409 gate_real32_t radius_meters, gate_enumint_t image_type,
410 gate_uint16_t image_width, gate_uint16_t image_height,
411 gate_blob_t* png_data)
412 {
413 3 gate_result_t ret = GATE_RESULT_FAILED;
414 3 gate_string_t subscription_key_value = GATE_INIT_EMPTY;
415 3 gate_strbuilder_t builder = GATE_INIT_EMPTY;
416 char url_buffer[1024];
417 3 gate_string_t url = GATE_INIT_EMPTY;
418 3 gate_uri_t uri = GATE_INIT_EMPTY;
419 3 gate_map_t http_headers = GATE_INIT_EMPTY;
420 3 gate_memstream_t* response_stream = NULL;
421
422 do
423 {
424 gate_uint16_t zoom_factor;
425 3 gate_uint32_t status_code = 0;
426 3 char const* const image_type_str = azuremaps_get_image_type_string(image_type);
427 3 gate_uint16_t const max_zoom_factor =azuremaps_get_max_zoom_factor(image_type);
428 static gate_string_t const subscription_key_header = GATE_STRING_INIT_STATIC("subscription-key");
429
430
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (gate_string_is_empty(api_key))
431 {
432 3 azuremaps_read_api_key(&subscription_key_value);
433 }
434 else
435 {
436 gate_string_duplicate(&subscription_key_value, api_key);
437 }
438
439
440 3 gate_strbuilder_create_static(&builder, url_buffer, sizeof(url_buffer), 0);
441
442 3 zoom_factor = azuremaps_calc_zoom_factor(radius_meters, image_width, max_zoom_factor);
443
444 3 gate_strbuilder_append(&builder,
445 GATE_PRINT_CSTR, "https://atlas.microsoft.com/map/static?api-version=2024-04-01",
446 GATE_PRINT_CSTR, "&center=", GATE_PRINT_R64, longitude, GATE_PRINT_CSTR, ",", GATE_PRINT_R64, latitude,
447 GATE_PRINT_CSTR, "&zoom=", GATE_PRINT_UI16, zoom_factor,
448 GATE_PRINT_CSTR, "&width=", GATE_PRINT_UI16, image_width,
449 GATE_PRINT_CSTR, "&height=", GATE_PRINT_UI16, image_height,
450 GATE_PRINT_CSTR, "&tilesetId=", GATE_PRINT_CSTR, image_type_str,
451 GATE_PRINT_END);
452
453
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (NULL == gate_strbuilder_to_string(&builder, &url))
454 {
455 ret = GATE_RESULT_OUTOFMEMORY;
456 break;
457 }
458
459 3 ret = gate_uri_parse(&uri, &url);
460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
461
462 3 response_stream = gate_memstream_create(0);
463
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (response_stream == NULL)
464 {
465 ret = GATE_RESULT_OUTOFMEMORY;
466 break;
467 }
468
469
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (NULL == gate_util_stringmap_create(&http_headers))
470 {
471 ret = GATE_RESULT_OUTOFMEMORY;
472 break;
473 }
474 3 gate_util_stringmap_add_string(&http_headers, &subscription_key_header, &subscription_key_value);
475
476 3 ret = gate_tech_webapi_get(context, &uri, &http_headers, &status_code, (gate_stream_t*)response_stream, NULL);
477
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (status_code != 200)
479 {
480 ret = GATE_RESULT_EXECUTIONFAILED;
481 break;
482 }
483
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (NULL == gate_blob_create(png_data, gate_memstream_get_data(response_stream), gate_memstream_size(response_stream)))
484 {
485 ret = GATE_RESULT_OUTOFMEMORY;
486 break;
487 }
488
489 /* success state */
490 3 ret = GATE_RESULT_OK;
491 } while (0);
492
493
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (response_stream)
494 {
495 3 gate_object_release(response_stream);
496 }
497 3 gate_map_destroy(&http_headers);
498 3 gate_uri_destroy(&uri);
499 3 gate_string_release(&url);
500 3 gate_strbuilder_release(&builder);
501 3 gate_string_release(&subscription_key_value);
502 3 return ret;
503 }
504
505 3 static gate_result_t azuremaps_load_map_info(gate_tech_webapi_context_t* context,
506 gate_tech_webapi_geomaps_request_t* request,
507 gate_tech_webapi_geomaps_response_t* response)
508 {
509 gate_result_t ret;
510 do
511 {
512
513 9 ret = azuremaps_fetch_static_image(context, NULL,
514 request->center_longitude, request->center_latitude, request->radius_meters,
515 3 request->image_type, request->image_width, request->image_height, &response->image_data);
516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
517
518 3 response->image_width = request->image_width;
519 3 response->image_height = request->image_height;
520 3 response->center_latitude = request->center_latitude;
521 3 response->center_longitude = request->center_longitude;
522
523 {
524 3 const gate_uint16_t max_zoom_factor = azuremaps_get_max_zoom_factor(request->image_type);
525 3 const gate_uint16_t zoom_factor = azuremaps_calc_zoom_factor(request->radius_meters, request->image_width, max_zoom_factor);
526 3 const gate_real32_t meters_per_pixel = get_meters_per_pixel(request->center_latitude, zoom_factor);
527 3 const gate_real32_t half_width = meters_per_pixel * request->image_width * 0.5f;
528 3 const gate_real32_t half_height = meters_per_pixel * request->image_height * 0.5f;
529 3 const gate_real32_t x = get_mercator_meters_from_geopos_x(request->center_longitude);
530 3 const gate_real32_t y = get_mercator_meters_from_geopos_y(request->center_latitude);
531
532 3 response->min_longitude = get_longitude_from_mercator_meters(x - half_width);
533 3 response->min_latitude = get_latitude_from_mercator_meters(y - half_height);
534 3 response->max_longitude = get_longitude_from_mercator_meters(x + half_width);
535 3 response->max_latitude = get_latitude_from_mercator_meters(y + half_height);
536
537 3 response->width_meters = meters_per_pixel * request->image_width;
538 3 response->height_meters = meters_per_pixel * request->image_height;
539 }
540
541 3 response->image_type = request->image_type;
542 3 response->image_format = GATE_TECH_GEOMAPS_IMAGE_PNG;
543 } while (0);
544 3 return ret;
545 }
546
547 3 gate_result_t gate_tech_webapi_geomaps(gate_tech_webapi_context_t* context,
548 gate_tech_webapi_geomaps_request_t* request,
549 gate_tech_webapi_geomaps_response_t* response)
550 {
551 3 return azuremaps_load_map_info(context, request, response);
552 }
553