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