Revise timeout limits for downloading map tiles
[platform/core/location/maps-plugin-mapzen.git] / src / mapzen / tangram_view.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 extern "C" {
18 #include "mapzen_queue.h"
19 #include "mapzen_debug.h"
20 #include "mapzen_util.h"
21 #include "mapzen_restcurl.h"
22 }
23
24 #include "mapzen_plugin_internal.h"
25 #include "tangram_view.hpp"
26 #include <tangram.h>
27 #include <src/platform_tizen.h>
28 #include <Elementary.h>
29
30 #include <string>
31 #include <cstdio>
32
33 #include <sys/syscall.h> // SYS_gettid
34
35 #define NORMAL_SCENE_FILE_PATH "/usr/share/maps/mapzen/scenes/bubble-wrap/bubble-wrap.yaml"
36 #define TERRAIN_SCENE_FILE_PATH "/usr/share/maps/mapzen/scenes/walkabout-style/walkabout-style.yaml"
37
38
39 TangramView::TangramView()
40 {
41         // Nothing to do.
42 }
43
44 TangramView::~TangramView()
45 {
46         // Nothing to do.
47 }
48
49 mapzen_error_e TangramView::create(maps_view_h view, maps_plugin_map_view_ready_cb callback, const char* providerKey)
50 {
51         if (!view) {
52                 return MAPZEN_ERROR_INVALID_PARAMETER;
53         }
54
55         int maps_error = MAPS_ERROR_NONE;
56         maps_error = maps_view_get_viewport(view, &m_image);
57         if (maps_error != MAPS_ERROR_NONE) {
58                 return (mapzen_error_e)convert_maps_error_to_mapzen_error(maps_error);
59         }
60
61         maps_error = maps_view_get_screen_location(view, &m_x, &m_y, &m_w, &m_h);
62         if (maps_error != MAPS_ERROR_NONE) {
63                 return (mapzen_error_e)convert_maps_error_to_mapzen_error(maps_error);
64         }
65
66         // Create an OpenGL context.
67         {
68                 m_config = evas_gl_config_new();
69                 if (!m_config) {
70                         MAPS_LOGE("evas_gl_config_new() failed");
71                         return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
72                 }
73
74                 m_config->color_format = EVAS_GL_RGBA_8888;
75                 m_config->depth_bits   = EVAS_GL_DEPTH_BIT_24;
76                 m_config->stencil_bits = EVAS_GL_STENCIL_NONE;
77                 m_config->options_bits = EVAS_GL_OPTIONS_DIRECT;
78
79                 m_gl = evas_gl_new(evas_object_evas_get(m_image));
80                 if (!m_gl) {
81                         MAPS_LOGE("evas_gl_new() failed");
82                         return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
83                 }
84
85                 m_api = evas_gl_api_get(m_gl);
86                 if (!m_api) {
87                         MAPS_LOGE("evas_gl_api_get() failed");
88                         return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
89                 }
90
91                 m_context = evas_gl_context_create(m_gl, nullptr);
92                 if (!m_context) {
93                         MAPS_LOGE("evas_gl_context_create() failed");
94                         return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
95                 }
96         }
97
98         // Set up rendering surface.
99         auto error = setupOpenGlSurface(view);
100         if (error != MAPZEN_ERROR_NONE) {
101                 return error;
102         }
103
104         m_readyCb = callback;
105         // TODO: What to do for multiple instances
106         Tangram::setEvasGlAPI(m_api);
107
108         Tangram::UrlClient::Options urlOptions;
109         char* proxyAddress = NULL;
110         get_proxy_address(&proxyAddress);
111         if (proxyAddress) {
112                 urlOptions.proxyAddress = proxyAddress;
113                 free(proxyAddress);
114         }
115
116         urlOptions.connectionTimeoutMs = 0;
117         urlOptions.requestTimeoutMs = 120000;
118
119         m_platform = std::make_shared<Tangram::TizenPlatform>(urlOptions);
120
121         m_uiThreadId = syscall(SYS_gettid);
122
123         m_platform->setRenderCallbackFunction([&]() {
124
125                 if (m_uiThreadId == syscall(SYS_gettid)) {
126                         m_dirty = false;
127                         evas_object_image_pixels_dirty_set(m_image, EINA_TRUE);
128                 } else {
129                         m_dirty = true;
130                 }
131         });
132
133         // NB: Since there is no safe way to use ecore_main_loop_thread_safe_call_async in a
134         // module that could be unloaded at runtime we use polling to check on the mainloop
135         // whether a render request came in.
136         // TODO only run timer when async tasks (Tile- and Scene-Loading) are in progress.
137         m_renderRequestTimer = ecore_timer_add(0.05, [](void *data){
138                 auto tv = static_cast<TangramView*>(data);
139                 if (tv->m_dirty) {
140                         tv->m_dirty = false;
141                         evas_object_image_pixels_dirty_set((Evas_Object*)tv->m_image, EINA_TRUE);
142                 }
143                 return EINA_TRUE;
144         }, this);
145
146         // Set up the tangram map.
147         m_map.reset(new Tangram::Map(m_platform));
148
149         float scaleFactor = elm_config_scale_get();
150         MAPS_LOGD("evas_gl_context_create() set PixelScale %f", scaleFactor);
151         m_map->setPixelScale(scaleFactor);
152
153         if (providerKey) {
154                 m_providerKey = providerKey;
155         }
156
157         // Start loading the scene by setting the map type.
158         setMapType(view);
159
160         // Make the GL context current and perform GL setup.
161         evas_gl_make_current(m_gl, m_surface, m_context);
162         m_map->setupGL();
163
164         m_map->resize(m_w, m_h);
165
166         if (m_sceneLoaded && m_w > 1 && m_h > 1) {
167                 readyMapCb((void*)view);
168         }
169
170         m_isInitialized = true;
171
172         return MAPZEN_ERROR_NONE;
173 }
174
175 mapzen_error_e TangramView::setupOpenGlSurface(maps_view_h view)
176 {
177         if (!view || !m_gl || !m_config || !m_image || !m_context) {
178                 return MAPZEN_ERROR_INVALID_PARAMETER;
179         }
180
181         // Remove any previous pixel callback on the image.
182         evas_object_image_pixels_get_callback_set(m_image, nullptr, nullptr);
183
184         if (m_surface) {
185                 // Destroy the old surface.
186                 evas_object_image_native_surface_set(m_image, nullptr);
187                 evas_gl_surface_destroy(m_gl, m_surface);
188         }
189
190         m_w = MAX(m_w, 1);
191         m_h = MAX(m_h, 1);
192
193         evas_object_image_size_set(m_image, m_w, m_h);
194
195         Evas_Native_Surface native_surface;
196         m_surface = evas_gl_surface_create(m_gl, m_config, m_w, m_h);
197         if (!m_surface) {
198                 MAPS_LOGE("evas_gl_surface_create() failed");
199                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
200         }
201
202         if (!evas_gl_native_surface_get(m_gl, m_surface, &native_surface)) {
203                 // Could not get the native surface information, so destroy the surface and exit.
204                 evas_gl_make_current(m_gl, nullptr, nullptr);
205                 evas_gl_surface_destroy(m_gl, m_surface);
206                 m_surface = nullptr;
207                 MAPS_LOGE("evas_gl_native_surface_get() failed");
208                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
209         }
210
211         // Set the native surface information and pixel callback on the image.
212         evas_object_image_native_surface_set(m_image, &native_surface);
213         evas_object_image_pixels_get_callback_set(m_image, pixelGetCb, view);
214
215         return MAPZEN_ERROR_NONE;
216 }
217
218 mapzen_error_e TangramView::destroy(maps_view_h view)
219 {
220         if (!view) {
221                 return MAPZEN_ERROR_INVALID_PARAMETER;
222         }
223
224         m_isInitialized = false;
225         m_sceneLoaded = false;
226
227         if (m_renderRequestTimer) {
228                 ecore_timer_del(m_renderRequestTimer);
229                 m_renderRequestTimer = nullptr;
230         }
231
232         if (m_image) {
233                 evas_object_image_pixels_get_callback_set(m_image, nullptr, nullptr);
234         }
235
236         if (m_gl) {
237                 if (m_surface && m_context) {
238                         evas_gl_make_current(m_gl, m_surface, m_context);
239                 }
240         }
241
242         if (m_map) {
243                 removeAllObjects();
244
245                 m_map.reset();
246                 m_platform.reset();
247         }
248
249         if (m_gl) {
250                 if (m_surface) {
251                         evas_object_image_native_surface_set(m_image, nullptr);
252                         evas_gl_surface_destroy(m_gl, m_surface);
253                 }
254                 if (m_context) {
255                         evas_gl_context_destroy(m_gl, m_context);
256                 }
257                 evas_gl_free(m_gl);
258                 m_gl = nullptr;
259         }
260
261         if (m_config) {
262                 evas_gl_config_free(m_config);
263         }
264
265         return MAPZEN_ERROR_NONE;
266 }
267
268 mapzen_error_e TangramView::render(maps_view_h view, const maps_coordinates_h coord, double zoom, double angle)
269 {
270         if (!view || !coord) {
271                 MAPS_LOGE("Render requested with invalid 'view' or 'coord'.");
272                 return MAPZEN_ERROR_INVALID_PARAMETER;
273         }
274
275         if (!m_isInitialized) {
276                 MAPS_LOGE("Render requested with view that is not initialized.");
277                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
278         }
279
280         setMapType(view);
281
282         int x = 0, y = 0, w = 0, h = 0;
283         maps_view_get_screen_location(view, &x, &y, &w, &h);
284         m_w = MAX(m_w, 1);
285         m_h = MAX(m_h, 1);
286
287         if (x != m_x || y != m_y || w != m_w || h != m_h) {
288                 if ( (m_w <= 1 || m_h <= 1) && w > 1 && h > 1 && m_sceneLoaded) {
289                         // Send a tangram view ready callback when we have a legit canvas size
290                         readyMapCb((void*)view);
291                 }
292                 m_x = x;
293                 m_y = y;
294                 m_w = w;
295                 m_h = h;
296                 evas_gl_make_current(m_gl, m_surface, m_context);
297                 m_map->resize(m_w, m_h);
298                 setupOpenGlSurface(view);
299         }
300
301         if (m_zoom != zoom) {
302                 m_zoom = zoom;
303                 m_map->setZoom(zoom);
304         }
305
306         if (m_angle != angle) {
307                 m_angle = angle;
308                 m_map->setRotation(degrees_to_radians(angle));
309         }
310
311         double lng = 0, lat = 0;
312         maps_coordinates_get_longitude(coord, &lng);
313         maps_coordinates_get_latitude(coord, &lat);
314
315         if (m_lng != lng || m_lat != lat) {
316                 m_lng = lng;
317                 m_lat = lat;
318                 m_map->setPosition(m_lng, m_lat);
319         }
320
321         m_map->getPlatform()->requestRender();
322
323         return MAPZEN_ERROR_NONE;
324 }
325
326 mapzen_error_e TangramView::moveCenter(maps_view_h view, int delta_x, int delta_y)
327 {
328         if (!view) {
329                 return MAPZEN_ERROR_INVALID_PARAMETER;
330         }
331
332         if (!m_isInitialized) {
333                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
334         }
335
336         if (delta_x == 0 && delta_y == 0) {
337                 return MAPZEN_ERROR_NONE;
338         }
339
340         // The delta_x and delta_y values are in pixels, so we need to determine the equivalent displacement in
341         // longitude and latitude to set the new center position.
342
343         double x = 0.5 * m_w + (double)delta_x;
344         double y = 0.5 * m_h + (double)delta_y;
345         double lng = 0.0, lat = 0.0;
346         if (m_map->screenPositionToLngLat(x, y, &lng, &lat)) {
347                 lng = wrap_longitude(lng);
348                 lat = wrap_latitude(lat);
349                 m_map->setPosition(lng, lat);
350         }
351
352         MAPS_LOGD("Moved with delta x: %d, y: %d to coordinates lon: %f, lat: %f", delta_x, delta_y, lng, lat);
353
354         return MAPZEN_ERROR_NONE;
355 }
356
357 mapzen_error_e TangramView::getCenter(maps_view_h view, maps_coordinates_h *center)
358 {
359         if (!view || !center) {
360                 return MAPZEN_ERROR_INVALID_PARAMETER;
361         }
362
363         if (!m_isInitialized) {
364                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
365         }
366
367         double longitude = 0, latitude = 0;
368         m_map->getPosition(longitude, latitude);
369
370         longitude = wrap_longitude(longitude);
371         latitude = wrap_latitude(latitude);
372         if (*center == nullptr) {
373                 maps_coordinates_create(latitude, longitude, center);
374         } else {
375                 maps_coordinates_set_latitude(*center, latitude);
376                 maps_coordinates_set_longitude(*center, longitude);
377         }
378
379         return MAPZEN_ERROR_NONE;
380 }
381
382 mapzen_error_e TangramView::setScalebarEnabled(maps_view_h view, bool enable)
383 {
384         return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
385 }
386
387 mapzen_error_e TangramView::getScalebarEnabled(maps_view_h view, bool *enabled)
388 {
389         return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
390 }
391
392 mapzen_error_e TangramView::convertScreenToGeolocation(maps_view_h view, int x, int y, maps_coordinates_h *coord)
393 {
394         if (!view || !coord) {
395                 return MAPZEN_ERROR_INVALID_PARAMETER;
396         }
397
398         if (!m_isInitialized) {
399                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
400         }
401
402         double longitude = (double)x, latitude = (double)y;
403         m_map->screenPositionToLngLat(x, y, &longitude, &latitude);
404
405         longitude = wrap_longitude(longitude);
406         latitude = wrap_latitude(latitude);
407         if (*coord == nullptr) {
408                 maps_coordinates_create(latitude, longitude, coord);
409         } else {
410                 maps_coordinates_set_latitude(*coord, latitude);
411                 maps_coordinates_set_longitude(*coord, longitude);
412         }
413
414         return MAPZEN_ERROR_NONE;
415 }
416
417 mapzen_error_e TangramView::convertGeolocationToScreen(maps_view_h view, const maps_coordinates_h coord, int *x, int *y)
418 {
419         if (!view || !coord) {
420                 return MAPZEN_ERROR_INVALID_PARAMETER;
421         }
422
423         if (!m_isInitialized) {
424                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
425         }
426
427         double lng, lat;
428         maps_coordinates_get_latitude(coord, &lat);
429         maps_coordinates_get_longitude(coord, &lng);
430
431         double screenx, screeny;
432         m_map->lngLatToScreenPosition(lng, lat, &screenx, &screeny);
433
434         *x = (int)screenx;
435         *y = (int)screeny;
436
437         return MAPZEN_ERROR_NONE;
438 }
439
440 mapzen_error_e TangramView::getMinZoomLevel(maps_view_h view, int *zoom)
441 {
442         *zoom = 0;
443         return MAPZEN_ERROR_NONE;
444 }
445
446 mapzen_error_e TangramView::getMaxZoomLevel(maps_view_h view, int *zoom)
447 {
448         *zoom = 20;
449         return MAPZEN_ERROR_NONE;
450 }
451
452 mapzen_error_e TangramView::onViewObject(maps_view_h view, const maps_view_object_h object, maps_view_object_operation_e operation)
453 {
454         if (!view || !object || operation < MAPS_VIEW_OBJECT_ADD || operation > MAPS_VIEW_OBJECT_REMOVE) {
455                 return MAPZEN_ERROR_INVALID_PARAMETER;
456         }
457
458         if (!m_isInitialized || !m_map) {
459                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
460         }
461
462         mapzen_error_e error = MAPZEN_ERROR_NONE;
463
464         TangramView *tv = nullptr;
465         int maps_error = maps_view_get_maps_plugin_view_handle(view, (void**)&tv);
466         if (maps_error != MAPS_ERROR_NONE || !tv) { return MAPZEN_ERROR_INVALID_PARAMETER; }
467
468         maps_view_object_type_e type = MAPS_VIEW_OBJECT_POLYLINE;
469         maps_view_object_get_type(object, &type);
470
471         if (type < MAPS_VIEW_OBJECT_POLYLINE || type > MAPS_VIEW_OBJECT_MARKER) { return MAPZEN_ERROR_INVALID_PARAMETER; }
472
473         auto& mapViewObjs = tv->mapViewObjects();
474         auto& mutex = tv->viewObjectMutex();
475         MapViewObjects::iterator iter;
476         MapViewObjects::iterator end;
477         {
478                 std::lock_guard<std::mutex> lock(mutex);
479                 iter = mapViewObjs.find(object);
480                 end = mapViewObjs.end();
481         }
482
483         switch(operation) {
484                 case MAPS_VIEW_OBJECT_ADD:
485                         error = tv->addObject(object);
486                         break;
487                 case MAPS_VIEW_OBJECT_SET_VISIBLE:
488                         if (iter != end) {
489                                 error = tv->setObjectVisible(object, iter->second);
490                         }
491                         break;
492                 case MAPS_VIEW_OBJECT_CHANGE:
493                         if (iter != end) {
494                                 error = tv->updateObject(object, iter->second);
495                         }
496                         break;
497                 case MAPS_VIEW_OBJECT_REMOVE:
498                         error = tv->removeObject(object);
499                         break;
500                 default: break;
501         }
502
503         const char *oper_str[20] = { "ADD", "SET_VISIBLE", "MOVE", "CHANGE", "REMOVE"};
504         const char *type_str[20] = { "POLYLINE", "POLYGON", "MARKER", "UNKNOWN"};
505
506         MAPS_LOGD("Done Processing View Object: type=%s, operation=%s, object=%p",
507                 (type >= MAPS_VIEW_OBJECT_POLYLINE && type <= MAPS_VIEW_OBJECT_MARKER) ? type_str[type] : "?",
508                 (operation >= MAPS_VIEW_OBJECT_ADD && operation <= MAPS_VIEW_OBJECT_REMOVE) ? oper_str[operation] : "?",
509                 object);
510
511         if (error != MAPZEN_ERROR_NONE) {
512                 MAPS_LOGD("Something went wrong in processing this ViewObject operation");
513         }
514
515         return error;
516 }
517
518 mapzen_error_e TangramView::addObject(maps_view_object_h object)
519 {
520         if (!object || !m_map) { return MAPZEN_ERROR_INVALID_PARAMETER; }
521
522         mapzen_error_e error = MAPZEN_ERROR_NONE;
523         Tangram::MarkerID tvMarker = 0;
524         maps_view_object_type_e type = MAPS_VIEW_OBJECT_POLYLINE;
525
526         maps_view_object_get_type(object, &type);
527
528         MapViewObjects::iterator iter;
529         MapViewObjects::iterator end;
530
531         {
532                 std::lock_guard<std::mutex> lock(m_viewObjectMutex);
533                 iter = m_mapViewObjs.find(object);
534                 end = m_mapViewObjs.end();
535         }
536         if (iter == end) {
537                 tvMarker = m_map->markerAdd();
538                 if (tvMarker) {
539                         error = updateObject(object, tvMarker);
540                         if (error == MAPZEN_ERROR_NONE) {
541                                 std::lock_guard<std::mutex> lock(m_viewObjectMutex);
542                                 m_mapViewObjs.insert(std::make_pair(object, tvMarker));
543                         } else {
544                                 m_map->markerRemove(tvMarker);
545                                 tvMarker = 0;
546                         }
547                 }
548         }
549
550         return error;
551 }
552
553 mapzen_error_e TangramView::setObjectVisible(maps_view_object_h object, Tangram::MarkerID tvMarker)
554 {
555         if (!object || !tvMarker || !m_map) { return MAPZEN_ERROR_INVALID_PARAMETER; }
556
557         bool visible = true;
558         maps_view_object_get_visible(object, &visible);
559
560         m_map->markerSetVisible(tvMarker, visible);
561         return MAPZEN_ERROR_NONE;
562 }
563
564 mapzen_error_e TangramView::updateObject(maps_view_object_h object, Tangram::MarkerID tvMarker)
565 {
566         if (!object || !tvMarker || !m_map) { return MAPZEN_ERROR_INVALID_PARAMETER; }
567
568         mapzen_error_e error = MAPZEN_ERROR_NONE;
569         maps_view_object_type_e type = MAPS_VIEW_OBJECT_POLYLINE;
570
571         maps_view_object_get_type(object, &type);
572
573         switch(type) {
574                 case MAPS_VIEW_OBJECT_MARKER:
575                         error = updateMarker(object, tvMarker);
576                         break;
577                 case MAPS_VIEW_OBJECT_POLYLINE:
578                         error = updatePolyline(object, tvMarker);
579                         break;
580                 case MAPS_VIEW_OBJECT_POLYGON:
581                         error = updatePolygon(object, tvMarker);
582                         break;
583                 default:
584                         break;
585         }
586
587         return error;
588 }
589
590 mapzen_error_e TangramView::getBitmapMarkerImage(maps_view_object_h object, unsigned char *&imgData, int &imgWidth, int &imgHeight) {
591         char *imgPath = nullptr;
592         Evas_Object *img = nullptr;
593         int imgSize = 0;
594         int error = MAPS_ERROR_NONE;
595
596         do {
597                 error = maps_view_object_marker_get_image_file(object, &imgPath);
598                 if (error != MAPS_ERROR_NONE || !imgPath) { break; }
599
600                 img = evas_object_image_add(evas_object_evas_get(m_image));
601                 evas_object_image_file_set(img, imgPath, nullptr);
602                 int err = evas_object_image_load_error_get(img);
603                 if (err != EVAS_LOAD_ERROR_NONE) {
604                         MAPS_LOGE("Failed to load marker image file: %s", imgPath);
605                         free(imgPath);
606                         break;
607                 }
608                 free(imgPath);
609
610                 evas_object_image_size_get(img, &imgWidth, &imgHeight);
611                 imgSize = imgWidth * imgHeight * 4;
612
613                 // Get Raw data pointer to the image
614                 const unsigned char *srcData = (unsigned char *)evas_object_image_data_get(img, EINA_FALSE);
615                 if (!srcData || imgSize <= 0) {
616                         MAPS_LOGE("Failed to get image data from the evas image for the marker");
617                         error = MAPS_ERROR_OUT_OF_MEMORY;
618                         break;
619                 }
620
621                 imgData = (unsigned char *)malloc(imgSize);
622                 if (!imgData) {
623                         MAPS_LOGE("Failed to malloc!! for image data");
624                         error = MAPS_ERROR_OUT_OF_MEMORY;
625                         break;
626                 }
627
628                 // convert data from rgba to bgra (also flip the image)
629                 for (int i = 0; i < imgHeight; i++) {
630                         for (int j = 0; j < imgWidth; j++) {
631                                 int offset = ((i * imgWidth) + j) * 4;
632                                 int offset_flip = (((imgHeight - 1 - i) * imgWidth) + j) * 4;
633                                 *(imgData + offset_flip)        = *(srcData + offset + 2);
634                                 *(imgData + offset_flip + 1)    = *(srcData + offset + 1);
635                                 *(imgData + offset_flip + 2)    = *(srcData + offset);
636                                 *(imgData + offset_flip + 3)    = *(srcData + offset + 3);
637                         }
638                 }
639
640         } while (0);
641
642         return (mapzen_error_e)convert_maps_error_to_mapzen_error(error);
643 }
644
645 mapzen_error_e TangramView::updateMarker(maps_view_object_h object, Tangram::MarkerID tvMarker)
646 {
647         maps_coordinates_h mapsCoord = nullptr;
648         double lat = 0.0, lng = 0.0;
649         int markerWidth = 0, markerHeight = 0;
650         int imgWidth = 0, imgHeight = 0;
651         int error = MAPS_ERROR_NONE;
652         unsigned char *imgData = nullptr;
653
654         const char styleFormat[] = "{ style: 'sdk-point-overlay', color: white, size: [%dpx, %dpx], collide: false, anchor: %s, transition: { [show, hide]: { time: 0s } } }";
655
656         static std::string anchor;
657
658         maps_view_marker_type_e type;
659         error = maps_view_object_marker_get_type(object, &type);
660
661         do {
662                 if (error != MAPS_ERROR_NONE || type < MAPS_VIEW_MARKER_PIN || type > MAPS_VIEW_MARKER_STICKER) { break; }
663
664                 error = getBitmapMarkerImage(object, imgData, imgWidth, imgHeight);
665
666                 if (error != MAPS_ERROR_NONE || !imgData) {
667                         MAPS_LOGE("Failed getting image data from marker object, with error code: %d", error);
668                         break;
669                 }
670                 m_map->markerSetBitmap(tvMarker, imgWidth, imgHeight, reinterpret_cast<unsigned int *>(imgData));
671                 free(imgData);
672
673                 anchor = "center";
674                 if (type == MAPS_VIEW_MARKER_PIN) {
675                         anchor = "top";
676                 }
677
678                 float scaleFactor = 1.0/elm_config_scale_get();
679                 error = maps_view_object_marker_get_size(object, &markerWidth, &markerHeight);
680                 if (error != MAPS_ERROR_NONE) { break; }
681
682                 /*
683                  * - Sets the marker dimension to be image dimension if both the specified marker dimensions are zero
684                  * - Sets the marker dimension such that aspect ratio is maintained when one of the marker dimensions is
685                  *   zero
686                  */
687                 if (markerWidth == 0 && markerHeight == 0) {
688                         markerWidth = imgWidth;
689                         markerHeight = imgHeight;
690                 } else if (markerWidth == 0 && markerHeight > 0) {
691                         markerWidth = (float)markerHeight * (float)imgWidth / (float)imgHeight;
692                 } else if (markerWidth > 0 && markerHeight == 0) {
693                         markerHeight = (float)markerWidth * (float)imgHeight / (float)imgWidth;
694                 }
695
696                 float scaledWidth = scaleFactor * markerWidth;
697                 float scaledHeight = scaleFactor * markerHeight;
698
699                 char styleString[256];
700                 std::snprintf(styleString, sizeof(styleString), styleFormat, int(scaledWidth), int(scaledHeight), anchor.c_str());
701
702                 MAPS_LOGD("Marker Style String: %s", styleString);
703                 m_map->markerSetStylingFromString(tvMarker, styleString);
704
705                 error = maps_view_object_marker_get_coordinates(object, &mapsCoord);
706                 if (error != MAPS_ERROR_NONE) { break; }
707
708                 error = maps_coordinates_get_longitude(mapsCoord, &lng);
709                 if (error != MAPS_ERROR_NONE) {
710                         maps_coordinates_destroy(mapsCoord);
711                         break;
712                 }
713
714                 error = maps_coordinates_get_latitude(mapsCoord, &lat);
715                 if (error != MAPS_ERROR_NONE) {
716                         maps_coordinates_destroy(mapsCoord);
717                         break;
718                 }
719
720                 maps_coordinates_destroy(mapsCoord);
721                 m_map->markerSetPoint(tvMarker, Tangram::LngLat(lng, lat));
722
723                 int drawOrder = 0;
724                 error = maps_view_object_marker_get_z_order(object, &drawOrder);
725                 if (error != MAPS_ERROR_NONE) { break; }
726                 m_map->markerSetDrawOrder(tvMarker, drawOrder);
727         } while (0);
728
729         return (mapzen_error_e)convert_maps_error_to_mapzen_error(error);
730 }
731
732 mapzen_error_e TangramView::updatePolyline(maps_view_object_h object, Tangram::MarkerID tvMarker)
733 {
734         int error = MAPS_ERROR_NONE;
735         Tangram::Coordinates coords = {};
736         unsigned char r = 0, g = 0, b = 0, a = 0;
737         int width = 0;
738
739         const char styleFormat[] = "{ style: 'ux-route-line-overlay', color: [%d, %d, %d, %d], width: %dpx, order: 500 }";
740
741         do {
742                 error = maps_view_object_polyline_get_color(object, &r, &g, &b, &a);
743                 if (error != MAPS_ERROR_NONE) { break; }
744
745                 error = maps_view_object_polyline_get_width(object, &width);
746                 if (error != MAPS_ERROR_NONE) { break; }
747
748                 char styleString[256];
749                 std::snprintf(styleString, sizeof(styleString), styleFormat, r, g, b, a, width);
750
751                 MAPS_LOGD("Polyline Style String: %s", styleString);
752
753                 m_map->markerSetStylingFromString(tvMarker, styleString);
754
755                 error = maps_view_object_polyline_foreach_point(object, emplaceCoord, &coords);
756                 if (error != MAPS_ERROR_NONE) { break; }
757
758                 m_map->markerSetPolyline(tvMarker, coords.data(), static_cast<int>(coords.size()));
759         } while (0);
760
761         return (mapzen_error_e)convert_maps_error_to_mapzen_error(error);
762 }
763
764 mapzen_error_e TangramView::updatePolygon(maps_view_object_h object, Tangram::MarkerID tvMarker)
765 {
766         int error = MAPS_ERROR_NONE;
767         Tangram::Coordinates coords = {};
768         unsigned char r = 0, g = 0, b = 0, a = 0;
769         const char styleFormat[] = "{ style: 'polygons', color: [%d, %d, %d, %d] }";
770
771         do {
772                 error = maps_view_object_polygon_get_fill_color(object, &r, &g, &b, &a);
773                 if (error != MAPS_ERROR_NONE) { break; }
774
775                 char styleString[256];
776                 std::snprintf(styleString, sizeof(styleString), styleFormat, r, g, b, a);
777
778                 MAPS_LOGD("Polygon Style String: %s", styleString);
779
780                 m_map->markerSetStylingFromString(tvMarker, styleString);
781
782                 error = maps_view_object_polygon_foreach_point(object, emplaceCoord, &coords);
783                 if (error != MAPS_ERROR_NONE) { break; }
784
785                 int count = (int)coords.size();
786                 m_map->markerSetPolygon(tvMarker, coords.data(), &count, 1);
787         } while (0);
788
789         return (mapzen_error_e)convert_maps_error_to_mapzen_error(error);
790 }
791
792 mapzen_error_e TangramView::removeObject(maps_view_object_h object)
793 {
794         if (!object || !m_map) { return MAPZEN_ERROR_INVALID_PARAMETER; }
795
796         mapzen_error_e error = MAPZEN_ERROR_NONE;
797
798         std::lock_guard<std::mutex> lock(m_viewObjectMutex);
799         auto iter = m_mapViewObjs.find(object);
800         if (iter != m_mapViewObjs.end()) {
801                 if (iter->second) {
802                         m_map->markerRemove(iter->second);
803                 }
804                 iter = m_mapViewObjs.erase(iter);
805                 error = MAPZEN_ERROR_NONE;
806         }
807
808         return error;
809 }
810
811 void TangramView::removeAllObjects()
812 {
813         std::lock_guard<std::mutex> lock(m_viewObjectMutex);
814         m_mapViewObjs.clear();
815         m_map->markerRemoveAll();
816 }
817
818 bool TangramView::emplaceCoord(int index, maps_coordinates_h coordinate, void *user_data)
819 {
820         if (!coordinate || !user_data) { return false; }
821
822         int error = 0;
823         double lng = 0.0, lat = 0.0;
824
825         error = maps_coordinates_get_longitude(coordinate, &lng);
826         if (error != MAPS_ERROR_NONE) {
827                 MAPS_LOGD("Not able to extract longitude from coordiate");
828                 return false;
829         }
830         error = maps_coordinates_get_latitude(coordinate, &lat);
831         if (error != MAPS_ERROR_NONE) {
832                 MAPS_LOGD("Not able to extract longitude from coordiate");
833                 return false;
834         }
835
836         MAPS_LOGD("[%d] %f, %f", index+1, lng, lat);
837
838         Tangram::Coordinates *coords = static_cast<Tangram::Coordinates*>(user_data);
839         coords->emplace_back(lng, lat);
840         return true;
841 }
842
843 mapzen_error_e TangramView::captureSnapshot(maps_view_h view, void **data, int *width, int *height, maps_view_colorspace_type_e *cs)
844 {
845         if (!view) {
846                 return MAPZEN_ERROR_INVALID_PARAMETER;
847         }
848
849         if (!m_isInitialized) {
850                 return MAPZEN_ERROR_SERVICE_NOT_AVAILABLE;
851         }
852
853         *data = (void*)malloc(m_w * m_h * sizeof(unsigned int));
854         if (!*data) {
855                 return MAPZEN_ERROR_OUT_OF_MEMORY;
856         }
857
858         pixelGetCb(view, nullptr);
859
860         unsigned int *pixels = (unsigned int*)malloc(m_w * m_h * sizeof(unsigned int));
861         if (!pixels) {
862                 return MAPZEN_ERROR_OUT_OF_MEMORY;
863         }
864
865         m_map->captureSnapshot(pixels);
866
867         for (int i = 0; i < m_h; ++i) {
868                 memcpy((unsigned int*)*data + m_w * i, pixels + m_w * (m_h - i - 1), m_w * sizeof(unsigned int));
869         }
870
871         *cs = MAPS_VIEW_COLORSPACE_RGBA8888;
872         *width = m_w;
873         *height = m_h;
874
875         free(pixels);
876         return MAPZEN_ERROR_NONE;
877 }
878
879
880 void TangramView::setMapType(maps_view_h view)
881 {
882         if (!view) {
883                 return;
884         }
885
886         std::vector<Tangram::SceneUpdate> sceneUpdates;
887         maps_view_type_e map_type;
888         maps_view_get_type(view, &map_type);
889
890         tangram_view_type newViewType = (tangram_view_type)convert_maps_view_type_to_tangram_view_type(map_type);
891
892         // If Scene is being reloaded then reset all the scene preferences to their defaults (building/transit/language/providerKey)
893         if (newViewType != m_viewType) {
894                 m_buildingsEnabled = true;
895                 m_publicTransitEnabled = false;
896                 m_language = "en";
897                 m_isProviderKeySet = false;
898         }
899
900         bool buildings_enabled = false;
901         maps_view_get_buildings_enabled(view, &buildings_enabled);
902         if (buildings_enabled != m_buildingsEnabled) {
903                 m_buildingsEnabled = buildings_enabled;
904                 if (m_buildingsEnabled) {
905                         sceneUpdates.push_back( {"global.sdk_building_extrude", "true"} );
906                 } else {
907                         sceneUpdates.push_back( {"global.sdk_building_extrude", "false"} );
908                 }
909         }
910
911         bool public_transit_enabled = false;
912         maps_view_get_public_transit_enabled(view, &public_transit_enabled);
913         if (public_transit_enabled != m_publicTransitEnabled) {
914                 m_publicTransitEnabled = public_transit_enabled;
915                 if (m_publicTransitEnabled) {
916                         sceneUpdates.push_back( {"global.sdk_transit_overlay", "true"} );
917                 } else {
918                         sceneUpdates.push_back( {"global.sdk_transit_overlay", "false"} );
919                 }
920         }
921
922         char* language = nullptr;
923         maps_view_get_language(view, &language);
924         if (m_language != language) {
925                 m_language = language;
926                 sceneUpdates.push_back( {"global.ux_language", m_language.substr(0, 2).c_str()} );
927         }
928         free(language);
929
930         // set provider key for vector tiles
931         if (m_providerKey.size() > 0 && !m_isProviderKeySet) {
932                 // block to hide key in log
933                 //MAPS_LOGD("Queueing API key update: %s", m_providerKey.c_str());
934                 sceneUpdates.push_back( {"global.sdk_mapzen_api_key", m_providerKey.c_str()} );
935                 m_isProviderKeySet = true;
936         }
937
938         // Loading a new tangram scene resets the map states/caches.
939         if (newViewType != m_viewType) {
940                 // When the scene is changed, update the 'sdk-point-overlay' style to remove it's texture;
941                 // this allows us to use this style for markers with custom bitmaps.
942                 sceneUpdates.push_back( {"styles.sdk-point-overlay.texture", "null"} );
943
944                 const char* newSceneFile = NORMAL_SCENE_FILE_PATH;
945                 switch(newViewType) {
946                         case TANGRAM_VIEW_TERRAIN:
947                                 newSceneFile = TERRAIN_SCENE_FILE_PATH;
948                                 break;
949                         case TANGRAM_VIEW_NORMAL:
950                                 newSceneFile = NORMAL_SCENE_FILE_PATH;
951                                 break;
952                         default:
953                                 return;
954                 }
955                 m_viewType = newViewType;
956                 m_map->loadSceneAsync(newSceneFile, false, sceneLoadedCb, (void*)view, sceneUpdates);
957         } else {
958                 // bool traffic_enabled = false;
959                 // maps_view_get_traffic_enabled(view, &traffic_enabled);
960                 if (sceneUpdates.empty()) { return; }
961                 m_map->queueSceneUpdate(sceneUpdates);
962                 m_map->applySceneUpdates();
963         }
964 }
965
966 void TangramView::sceneLoadedCb(void *data) {
967         TangramView *tv = nullptr;
968         int maps_error = maps_view_get_maps_plugin_view_handle((maps_view_h)data, (void**)&tv);
969         if (maps_error != MAPS_ERROR_NONE || !tv) { return; }
970
971         // only do this for first scene load not for any sceneUpdates
972         if (!tv->isSceneLoaded()) {
973                 if (tv->getWidth() > 1 && tv->getHeight() > 1) {
974                         readyMapCb(data);
975                 }
976                 tv->setSceneLoaded(true);
977         }
978 }
979
980 void TangramView::readyMapCb(void *data)
981 {
982         TangramView *tv = nullptr;
983         int maps_error = maps_view_get_maps_plugin_view_handle((maps_view_h)data, (void**)&tv);
984         if (maps_error != MAPS_ERROR_NONE || !tv) { return; }
985
986         if (tv->m_readyCb) {
987                 tv->m_readyCb(data);
988         }
989 }
990
991 void TangramView::pixelGetCb(void *data, Evas_Object *obj)
992 {
993         TangramView *tv = nullptr;
994         int maps_error = maps_view_get_maps_plugin_view_handle(data, (void**)&tv);
995         if (maps_error != MAPS_ERROR_NONE || !tv) {
996                 return;
997         }
998
999         tv->m_dirty = false;
1000
1001         if (!tv->m_gl || !tv->m_surface || !tv->m_context) {
1002                 return;
1003         }
1004
1005         evas_gl_make_current(tv->m_gl, tv->m_surface, tv->m_context);
1006         tv->m_api->glViewport(0, 0, tv->m_w, tv->m_h);
1007         if (tv->m_map) {
1008                 tv->m_map->update(0);
1009                 tv->m_map->render();
1010                 MAPS_LOGD("pixelGetCb called on TangramView");
1011         } else {
1012                 MAPS_LOGD("tv->m_map is null, in pixelGelCb");
1013         }
1014 }