Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.mapbox.mapboxsdk.maps;

class Image {
private final byte[] buffer;
private final float pixelRatio;
private final String name;
private final int width;
private final int height;

public Image(byte[] buffer, float pixelRatio, String name, int width, int height) {
this.buffer = buffer;
this.pixelRatio = pixelRatio;
this.name = name;
this.width = width;
this.height = height;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.mapbox.services.commons.geojson.Geometry;

import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;

import timber.log.Timber;
Expand Down Expand Up @@ -466,6 +467,13 @@ public void addImage(@NonNull String name, @NonNull Bitmap image) {
nativeMapView.addImage(name, image);
}

/**
* Adds an images to be used in the map's style
*/
public void addImages(@NonNull HashMap<String, Bitmap> images) {
nativeMapView.addImages(images);
}

/**
* Removes an image from the map's style
*
Expand Down Expand Up @@ -1670,9 +1678,9 @@ public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBoun
/**
* Get a camera position that fits a provided shape with a given bearing and padding.
*
* @param geometry the geometry to constrain the map with
* @param bearing the bearing at which to compute the geometry's bounds
* @param padding the padding to apply to the bounds
* @param geometry the geometry to constrain the map with
* @param bearing the bearing at which to compute the geometry's bounds
* @param padding the padding to apply to the bounds
* @return the camera position that fits the bounds and padding
*/
public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.AsyncTask;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
Expand Down Expand Up @@ -34,7 +35,9 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import timber.log.Timber;

Expand Down Expand Up @@ -746,6 +749,7 @@ public void addImage(@NonNull String name, @NonNull Bitmap image) {
if (isDestroyedOn("addImage")) {
return;
}

// Check/correct config
if (image.getConfig() != Bitmap.Config.ARGB_8888) {
image = image.copy(Bitmap.Config.ARGB_8888, false);
Expand All @@ -762,6 +766,14 @@ public void addImage(@NonNull String name, @NonNull Bitmap image) {
nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array());
}

public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) {
if (isDestroyedOn("addImages")) {
return;
}
//noinspection unchecked
new BitmapImageConversionTask(this).execute(bitmapHashMap);
}

public void removeImage(String name) {
if (isDestroyedOn("removeImage")) {
return;
Expand Down Expand Up @@ -1006,6 +1018,8 @@ private native void nativeFlyTo(double angle, double latitude, double longitude,
private native void nativeAddImage(String name, int width, int height, float pixelRatio,
byte[] array);

private native void nativeAddImages(Image[] images);

private native void nativeRemoveImage(String name);

private native Bitmap nativeGetImage(String name);
Expand Down Expand Up @@ -1093,4 +1107,55 @@ public void run() {

});
}


//
// Image conversion
//

private static class BitmapImageConversionTask extends AsyncTask<HashMap<String, Bitmap>, Void, List<Image>> {

private NativeMapView nativeMapView;

BitmapImageConversionTask(NativeMapView nativeMapView) {
this.nativeMapView = nativeMapView;
}

@Override
protected List<Image> doInBackground(HashMap<String, Bitmap>... params) {
HashMap<String, Bitmap> bitmapHashMap = params[0];

List<Image> images = new ArrayList<>();
ByteBuffer buffer;
String name;
Bitmap bitmap;

for (Map.Entry<String, Bitmap> stringBitmapEntry : bitmapHashMap.entrySet()) {
name = stringBitmapEntry.getKey();
bitmap = stringBitmapEntry.getValue();

if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
}

buffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer);

float density = bitmap.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : bitmap.getDensity();
float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT;

images.add(new Image(buffer.array(), pixelRatio, name, bitmap.getWidth(), bitmap.getHeight()));
}

return images;
}

@Override
protected void onPostExecute(List<Image> images) {
super.onPostExecute(images);
if (nativeMapView != null && !nativeMapView.isDestroyedOn("nativeAddImages")) {
nativeMapView.nativeAddImages(images.toArray(new Image[images.size()]));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.mapbox.mapboxsdk.testapp.activity.style;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.support.v7.app.AppCompatActivity;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;

import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
Expand All @@ -32,7 +33,7 @@
import com.mapbox.services.commons.models.Position;

import java.io.IOException;

import java.util.HashMap;
import java.util.List;

import timber.log.Timber;
Expand Down Expand Up @@ -64,49 +65,10 @@ public void onCreate(Bundle savedInstanceState) {
}

@Override
public void onMapReady(MapboxMap map) {
public void onMapReady(final MapboxMap map) {
mapboxMap = map;
try {
// read local geojson from raw folder
String tinyCountriesJson = ResourceUtils.readRawResource(this, R.raw.tiny_countries);

// convert geojson to a model
FeatureCollection featureCollection = new GsonBuilder()
.registerTypeAdapter(Geometry.class, new GeometryDeserializer())
.registerTypeAdapter(Position.class, new PositionDeserializer())
.create().fromJson(tinyCountriesJson, FeatureCollection.class);

// add a geojson to the map
Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
mapboxMap.addSource(source);

// for each feature add a symbolLayer
for (Feature feature : featureCollection.getFeatures()) {
String countryName = feature.getStringProperty(FEATURE_ID);

// create View
TextView textView = new TextView(this);
textView.setBackgroundColor(getResources().getColor(R.color.blueAccent));
textView.setPadding(10, 5, 10, 5);
textView.setTextColor(Color.WHITE);
textView.setText(countryName);

// create bitmap from view
mapboxMap.addImage(countryName, SymbolGenerator.generate(textView));
}

// create layer use
mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
.withProperties(
iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
iconAllowOverlap(false)
)
);

addSymbolClickListener();
} catch (IOException exception) {
Timber.e(exception);
}
addSymbolClickListener();
new LoadDataTask(map, SymbolGeneratorActivity.this).execute();
}

private void addSymbolClickListener() {
Expand Down Expand Up @@ -213,4 +175,91 @@ public static Bitmap generate(@NonNull View view) {
return bitmap;
}
}
}

private static class LoadDataTask extends AsyncTask<Void, Void, FeatureCollection> {

private final MapboxMap mapboxMap;
private final Context context;

LoadDataTask(MapboxMap mapboxMap, Context context) {
this.mapboxMap = mapboxMap;
this.context = context;
}

@Override
protected FeatureCollection doInBackground(Void... params) {
try {
// read local geojson from raw folder
String tinyCountriesJson = ResourceUtils.readRawResource(context, R.raw.tiny_countries);

// convert geojson to a model
FeatureCollection featureCollection = new GsonBuilder()
.registerTypeAdapter(Geometry.class, new GeometryDeserializer())
.registerTypeAdapter(Position.class, new PositionDeserializer())
.create().fromJson(tinyCountriesJson, FeatureCollection.class);

return featureCollection;
} catch (IOException exception) {
return null;
}
}


@Override
protected void onPostExecute(FeatureCollection featureCollection) {
super.onPostExecute(featureCollection);
if (featureCollection == null) {
return;
}

// add a geojson to the map
Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
mapboxMap.addSource(source);

// create layer use
mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
.withProperties(
iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
iconAllowOverlap(false)
)
);

new GenerateSymbolTask(mapboxMap, context).execute(featureCollection);
}
}

private static class GenerateSymbolTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> {

private MapboxMap mapboxMap;
private Context context;

GenerateSymbolTask(MapboxMap mapboxMap, Context context) {
this.mapboxMap = mapboxMap;
this.context = context;
}

@SuppressWarnings("WrongThread")
@Override
protected HashMap<String, Bitmap> doInBackground(FeatureCollection... params) {
FeatureCollection featureCollection = params[0];

HashMap<String, Bitmap> imagesMap = new HashMap<>();
for (Feature feature : featureCollection.getFeatures()) {
String countryName = feature.getStringProperty(FEATURE_ID);
TextView textView = new TextView(context);
textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent));
textView.setPadding(10, 5, 10, 5);
textView.setTextColor(Color.WHITE);
textView.setText(countryName);
imagesMap.put(countryName, SymbolGenerator.generate(textView));
}
return imagesMap;
}

@Override
protected void onPostExecute(HashMap<String, Bitmap> bitmapHashMap) {
super.onPostExecute(bitmapHashMap);
mapboxMap.addImages(bitmapHashMap);
}
}
}
2 changes: 2 additions & 0 deletions platform/android/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ add_library(mbgl-android STATIC
platform/android/src/style/conversion/types_string_values.hpp
platform/android/src/map/camera_position.cpp
platform/android/src/map/camera_position.hpp
platform/android/src/map/image.cpp
platform/android/src/map/image.hpp

# Style conversion Java -> C++
platform/android/src/style/android_conversion.hpp
Expand Down
1 change: 1 addition & 0 deletions platform/android/src/jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ void registerNatives(JavaVM *vm) {

// Map
CameraPosition::registerNative(env);
Image::registerNative(env);

// Connectivity
ConnectivityListener::registerNative(env);
Expand Down
44 changes: 44 additions & 0 deletions platform/android/src/map/image.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <mbgl/style/image.hpp>
#include <mbgl/util/exception.hpp>
#include "image.hpp"

namespace mbgl {
namespace android {

mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object<Image> image) {
static auto widthField = Image::javaClass.GetField<jni::jint>(env, "width");
static auto heightField = Image::javaClass.GetField<jni::jint>(env, "height");
static auto pixelRatioField = Image::javaClass.GetField<jni::jfloat>(env, "pixelRatio");
static auto bufferField = Image::javaClass.GetField<jni::Array<jbyte>>(env, "buffer");
static auto nameField = Image::javaClass.GetField<jni::String>(env, "name");

auto height = image.Get(env, heightField);
auto width = image.Get(env, widthField);
auto pixelRatio = image.Get(env, pixelRatioField);
auto pixels = image.Get(env, bufferField);
auto name = jni::Make<std::string>(env, image.Get(env, nameField));

jni::NullCheck(env, &pixels);
std::size_t size = pixels.Length(env);

mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
if (premultipliedImage.bytes() != uint32_t(size)) {
throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
}

jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));

return mbgl::style::Image {name, std::move(premultipliedImage), pixelRatio};
}

void Image::registerNative(jni::JNIEnv &env) {
// Lookup the class
Image::javaClass = *jni::Class<Image>::Find(env).NewGlobalRef(env).release();
}

jni::Class<Image> Image::javaClass;


} // namespace android
} // namespace mb

Loading