Add interface for stretchable image (#314)

* Add interface for stretch image

* Add demo for stretch image api

* Add stretchable image demp

* Update stretchable image test activity.

* Revert change for SymbolLayerActivity

This reverts commit 22bbb798
This commit is contained in:
Kevin Li
2020-04-16 14:31:38 +08:00
committed by GitHub
parent 77d0d5e5cf
commit 1cb8fb7a01
13 changed files with 760 additions and 7 deletions

View File

@@ -13,6 +13,9 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object<Image>& i
static auto bufferField = javaClass.GetField<jni::Array<jbyte>>(env, "buffer");
static auto nameField = javaClass.GetField<jni::String>(env, "name");
static auto sdfField = javaClass.GetField<jni::jboolean>(env, "sdf");
static auto contentField = javaClass.GetField<jni::Array<jfloat >>(env, "content");
static auto stretchXField = javaClass.GetField<jni::Array<jfloat>>(env, "stretchX");
static auto stretchYField = javaClass.GetField<jni::Array<jfloat>>(env, "stretchY");
auto height = image.Get(env, heightField);
auto width = image.Get(env, widthField);
@@ -20,7 +23,9 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object<Image>& i
auto pixels = image.Get(env, bufferField);
auto name = jni::Make<std::string>(env, image.Get(env, nameField));
auto sdf = (bool) image.Get(env, sdfField);
auto content = image.Get(env, contentField);
auto stretchX = image.Get(env, stretchXField);
auto stretchY = image.Get(env, stretchYField);
jni::NullCheck(env, pixels.get());
std::size_t size = pixels.Length(env);
@@ -29,8 +34,38 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object<Image>& i
throw mbgl::util::StyleImageException("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, sdf};
jni::GetArrayRegion(env, *pixels, 0, size,
reinterpret_cast<jbyte *>(premultipliedImage.data.get()));
style::ImageStretches imageStretchesX = {};
style::ImageStretches imageStretchesY = {};
if (stretchX.get()) {
std::size_t sizeX = stretchX.Length(env);
for (jni::jsize i = 0; i < sizeX - 1; i += 2) {
imageStretchesX.push_back({stretchX.Get(env, i), stretchX.Get(env, i + 1)});
}
}
if (stretchY.get()) {
std::size_t sizeY = stretchY.Length(env);
for (jni::jsize i = 0; i < sizeY - 1; i += 2) {
imageStretchesY.push_back({stretchY.Get(env, i), stretchY.Get(env, i + 1)});
}
}
if (content) {
const style::ImageContent imageContent = style::ImageContent{content.Get(env, 0),
content.Get(env, 1),
content.Get(env, 2),
content.Get(env, 3)};
return mbgl::style::Image{name, std::move(premultipliedImage), pixelRatio, sdf,
imageStretchesX,
imageStretchesY,
std::move(imageContent)};
}
return mbgl::style::Image{name, std::move(premultipliedImage), pixelRatio, sdf,
imageStretchesX,
imageStretchesY};
}
void Image::registerNative(jni::JNIEnv &env) {

View File

@@ -10,13 +10,24 @@ public class Image {
private final int width;
private final int height;
private final boolean sdf;
private final float[] content;
private final float[] stretchX;
private final float[] stretchY;
public Image(byte[] buffer, float pixelRatio, String name, int width, int height, boolean sdf) {
this(buffer, pixelRatio, name, width, height, sdf, null, null, null);
}
public Image(byte[] buffer, float pixelRatio, String name, int width, int height, boolean sdf,
float[] stretchX, float[] stretchY, float[] content) {
this.buffer = buffer;
this.pixelRatio = pixelRatio;
this.name = name;
this.width = width;
this.height = height;
this.sdf = sdf;
this.content = content;
this.stretchX = stretchX;
this.stretchY = stretchY;
}
}

View File

@@ -0,0 +1,66 @@
package com.mapbox.mapboxsdk.maps;
/**
* Describes the image content, e.g. where text can be fit into an image.
* <p>
* When sizing icons with icon-text-fit,
* the icon size will be adjusted so that the this content box fits exactly around the text.
*/
public final class ImageContent {
private final float left;
private final float top;
private final float right;
private final float bottom;
public ImageContent(float left,
float top,
float right,
float bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
/**
* Get the array for this content, sorted by left, top, right, bottom.
*
* @return the content array.
*/
public float[] getContentArray() {
float[] array = new float[4];
array[0] = left;
array[1] = top;
array[2] = right;
array[3] = bottom;
return array;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ImageContent)) {
return false;
}
ImageContent other = (ImageContent) obj;
return this.left == other.left
&& this.top == other.top
&& this.right == other.right
&& this.bottom == other.bottom;
}
@Override
public int hashCode() {
int result = (left != +0.0f ? Float.floatToIntBits(left) : 0);
result = 31 * result + (top != +0.0f ? Float.floatToIntBits(top) : 0);
result = 31 * result + (right != +0.0f ? Float.floatToIntBits(right) : 0);
result = 31 * result + (bottom != +0.0f ? Float.floatToIntBits(bottom) : 0);
return result;
}
@Override
public String toString() {
return "[ " + "left: " + left + ", " + "top: " + top + ", " + "right: " + right + ", " + "bottom: " + bottom + " ]";
}
}

View File

@@ -0,0 +1,54 @@
package com.mapbox.mapboxsdk.maps;
/**
* Describes the image stretch areas.
*/
public final class ImageStretches {
private final float first;
private final float second;
public ImageStretches(float first,
float second) {
this.first = first;
this.second = second;
}
/**
* The first stretchable part in pixel units.
*/
public float getFirst() {
return first;
}
/**
* The second stretchable part in pixel units.
*/
public float getSecond() {
return second;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ImageStretches)) {
return false;
}
ImageStretches other = (ImageStretches) obj;
return this.first == other.first
&& this.second == other.second;
}
@Override
public int hashCode() {
int result = (first != +0.0f ? Float.floatToIntBits(first) : 0);
result = 31 * result + (second != +0.0f ? Float.floatToIntBits(second) : 0);
return result;
}
@Override
public String toString() {
return "[ " + "first: " + first + ", " + "second: " + second + " ]";
}
}

View File

@@ -3,12 +3,9 @@ package com.mapbox.mapboxsdk.maps;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringDef;
import android.util.DisplayMetrics;
import android.util.Pair;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
@@ -26,6 +23,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringDef;
/**
* The proxy object for current map style.
* <p>
@@ -317,6 +319,20 @@ public class Style {
addImage(name, image, false);
}
/**
* Adds an image to be used in the map's style
*
* @param name the name of the image
* @param image the pre-multiplied Bitmap
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImage(@NonNull String name, @NonNull Bitmap image, @NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY, @Nullable ImageContent content) {
addImage(name, image, false, stretchX, stretchY, content);
}
/**
* Adds an drawable to be converted into a bitmap to be used in the map's style
*
@@ -331,6 +347,26 @@ public class Style {
addImage(name, bitmap, false);
}
/**
* Adds an drawable to be converted into a bitmap to be used in the map's style
*
* @param name the name of the image
* @param drawable the drawable instance to convert
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImage(@NonNull String name, @NonNull Drawable drawable,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable);
if (bitmap == null) {
throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap.");
}
addImage(name, bitmap, false, stretchX, stretchY, content);
}
/**
* Adds an image to be used in the map's style
*
@@ -343,6 +379,25 @@ public class Style {
nativeMap.addImages(new Image[] {toImage(new Builder.ImageWrapper(name, bitmap, sdf))});
}
/**
* Adds an image to be used in the map's style
*
* @param name the name of the image
* @param bitmap the pre-multiplied Bitmap
* @param sdf the flag indicating image is an SDF or template image
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImage(@NonNull final String name, @NonNull Bitmap bitmap, boolean sdf,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
validateState("addImage");
nativeMap.addImages(new Image[] {
toImage(new Builder.ImageWrapper(name, bitmap, sdf, stretchX, stretchY, content))});
}
/**
* Adds an image asynchronously, to be used in the map's style.
*
@@ -353,6 +408,22 @@ public class Style {
addImageAsync(name, image, false);
}
/**
* Adds an image asynchronously, to be used in the map's style.
*
* @param name the name of the image
* @param image the pre-multiplied Bitmap
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImageAsync(@NonNull String name, @NonNull Bitmap image,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
addImageAsync(name, image, false, stretchX, stretchY, content);
}
/**
* Adds an drawable asynchronously, to be converted into a bitmap to be used in the map's style.
*
@@ -367,6 +438,27 @@ public class Style {
addImageAsync(name, bitmap, false);
}
/**
* Adds an drawable asynchronously, to be converted into a bitmap to be used in the map's style.
*
* @param name the name of the image
* @param drawable the drawable instance to convert
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImageAsync(@NonNull String name, @NonNull Drawable drawable,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable);
if (bitmap == null) {
throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap.");
}
addImageAsync(name, bitmap, false, stretchX, stretchY, content);
}
/**
* Adds an image asynchronously, to be used in the map's style.
*
@@ -379,6 +471,25 @@ public class Style {
new BitmapImageConversionTask(nativeMap).execute(new Builder.ImageWrapper(name, bitmap, sdf));
}
/**
* Adds an image asynchronously, to be used in the map's style.
*
* @param name the name of the image
* @param bitmap the pre-multiplied Bitmap
* @param sdf the flag indicating image is an SDF or template image
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImageAsync(@NonNull final String name, @NonNull Bitmap bitmap, boolean sdf,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
validateState("addImage");
new BitmapImageConversionTask(nativeMap)
.execute(new Builder.ImageWrapper(name, bitmap, sdf, stretchX, stretchY, content));
}
/**
* Adds images to be used in the map's style.
*
@@ -388,6 +499,19 @@ public class Style {
addImages(images, false);
}
/**
* Adds images to be used in the map's style.
*
* @param images the map of images to add
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImages(@NonNull HashMap<String, Bitmap> images, @NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY, @Nullable ImageContent content) {
addImages(images, false, stretchX, stretchY, content);
}
/**
* Adds images to be used in the map's style.
*
@@ -406,6 +530,31 @@ public class Style {
nativeMap.addImages(convertedImages);
}
/**
* Adds images to be used in the map's style.
*
* @param images the map of images to add
* @param sdf the flag indicating image is an SDF or template image
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImages(@NonNull HashMap<String, Bitmap> images, boolean sdf,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
validateState("addImage");
Image[] convertedImages = new Image[images.size()];
int index = 0;
for (Builder.ImageWrapper imageWrapper
: Builder.ImageWrapper.convertToImageArray(images, sdf, stretchX, stretchY, content)) {
convertedImages[index] = toImage(imageWrapper);
index++;
}
nativeMap.addImages(convertedImages);
}
/**
* Adds images asynchronously, to be used in the map's style.
*
@@ -415,6 +564,19 @@ public class Style {
addImagesAsync(images, false);
}
/**
* Adds images asynchronously, to be used in the map's style.
*
* @param images the map of images to add
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImagesAsync(@NonNull HashMap<String, Bitmap> images, @NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY, @Nullable ImageContent content) {
addImagesAsync(images, false, stretchX, stretchY, content);
}
/**
* Adds images asynchronously, to be used in the map's style.
*
@@ -426,6 +588,24 @@ public class Style {
new BitmapImageConversionTask(nativeMap).execute(Builder.ImageWrapper.convertToImageArray(images, sdf));
}
/**
* Adds images asynchronously, to be used in the map's style.
*
* @param images the map of images to add
* @param sdf the flag indicating image is an SDF or template image
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
*/
public void addImagesAsync(@NonNull HashMap<String, Bitmap> images, boolean sdf,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
validateState("addImages");
new BitmapImageConversionTask(nativeMap)
.execute(Builder.ImageWrapper.convertToImageArray(images, sdf, stretchX, stretchY, content));
}
/**
* Removes an image from the map's style.
*
@@ -830,6 +1010,26 @@ public class Style {
return this.withImage(id, bitmap, false);
}
/**
* Will add the drawable as image when the map style has loaded.
*
* @param id the id for the image
* @param drawable the drawable to be converted and added
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
* @return this
*/
@NonNull
public Builder withImage(@NonNull String id, @NonNull Drawable drawable, @NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY, @Nullable ImageContent content) {
Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable);
if (bitmap == null) {
throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap.");
}
return this.withImage(id, bitmap, false, stretchX, stretchY, content);
}
/**
* Will add the drawables as images when the map style has loaded.
*
@@ -853,6 +1053,22 @@ public class Style {
return this.withImage(id, image, false);
}
/**
* Will add the image when the map style has loaded.
*
* @param id the id for the image
* @param image the image to be added
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
* @return this
*/
@NonNull
public Builder withImage(@NonNull String id, @NonNull Bitmap image, @NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY, @Nullable ImageContent content) {
return this.withImage(id, image, false, stretchX, stretchY, content);
}
/**
* Will add the images when the map style has loaded.
*
@@ -884,6 +1100,29 @@ public class Style {
return this.withImage(id, bitmap, sdf);
}
/**
* Will add the drawable as image when the map style has loaded.
*
* @param id the id for the image
* @param drawable the drawable to be converted and added
* @param sdf the flag indicating image is an SDF or template image
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
* @return this
*/
@NonNull
public Builder withImage(@NonNull String id, @NonNull Drawable drawable, boolean sdf,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY,
@Nullable ImageContent content) {
Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable);
if (bitmap == null) {
throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap.");
}
return this.withImage(id, bitmap, sdf, stretchX, stretchY, content);
}
/**
* Will add the drawables as images when the map style has loaded.
*
@@ -917,6 +1156,25 @@ public class Style {
return this;
}
/**
* Will add the image when the map style has loaded.
*
* @param id the id for the image
* @param image the image to be added
* @param sdf the flag indicating image is an SDF or template image
* @param stretchX image stretch areas for x axix
* @param stretchY image stretch areas for y axix
* @param content image content for text to fit
* @return this
*/
@NonNull
public Builder withImage(@NonNull String id, @NonNull Bitmap image, boolean sdf,
@NonNull List<ImageStretches> stretchX,
@NonNull List<ImageStretches> stretchY, @Nullable ImageContent content) {
images.add(new ImageWrapper(id, image, sdf, stretchX, stretchY, content));
return this;
}
/**
* Will add the images when the map style has loaded.
*
@@ -967,11 +1225,22 @@ public class Style {
Bitmap bitmap;
String id;
boolean sdf;
List<ImageStretches> stretchX;
List<ImageStretches> stretchY;
ImageContent content;
public ImageWrapper(String id, Bitmap bitmap, boolean sdf) {
this(id, bitmap, sdf, null, null, null);
}
public ImageWrapper(String id, Bitmap bitmap, boolean sdf, List<ImageStretches> stretchX,
List<ImageStretches> stretchY, ImageContent content) {
this.id = id;
this.bitmap = bitmap;
this.sdf = sdf;
this.stretchX = stretchX;
this.stretchY = stretchY;
this.content = content;
}
public Bitmap getBitmap() {
@@ -986,6 +1255,18 @@ public class Style {
return sdf;
}
public List<ImageStretches> getStretchX() {
return stretchX;
}
public List<ImageStretches> getStretchY() {
return stretchY;
}
public ImageContent getContent() {
return content;
}
public static ImageWrapper[] convertToImageArray(HashMap<String, Bitmap> bitmapHashMap, boolean sdf) {
ImageWrapper[] images = new ImageWrapper[bitmapHashMap.size()];
List<String> keyList = new ArrayList<>(bitmapHashMap.keySet());
@@ -995,6 +1276,19 @@ public class Style {
}
return images;
}
public static ImageWrapper[] convertToImageArray(HashMap<String, Bitmap> bitmapHashMap, boolean sdf,
List<ImageStretches> stretchX,
List<ImageStretches> stretchY,
ImageContent content) {
ImageWrapper[] images = new ImageWrapper[bitmapHashMap.size()];
List<String> keyList = new ArrayList<>(bitmapHashMap.keySet());
for (int i = 0; i < bitmapHashMap.size(); i++) {
String id = keyList.get(i);
images[i] = new ImageWrapper(id, bitmapHashMap.get(id), sdf, stretchX, stretchY, content);
}
return images;
}
}
public class LayerWrapper {
@@ -1059,6 +1353,24 @@ public class Style {
bitmap.copyPixelsToBuffer(buffer);
float pixelRatio = (float) bitmap.getDensity() / DisplayMetrics.DENSITY_DEFAULT;
if (imageWrapper.getStretchX() != null && imageWrapper.getStretchY() != null) {
float[] arrayX = new float[imageWrapper.getStretchX().size() * 2];
for (int i = 0; i < imageWrapper.getStretchX().size(); i++) {
arrayX[i * 2] = imageWrapper.getStretchX().get(i).getFirst();
arrayX[i * 2 + 1] = imageWrapper.getStretchX().get(i).getSecond();
}
float[] arrayY = new float[imageWrapper.getStretchY().size() * 2];
for (int i = 0; i < imageWrapper.getStretchY().size(); i++) {
arrayY[i * 2] = imageWrapper.getStretchY().get(i).getFirst();
arrayY[i * 2 + 1] = imageWrapper.getStretchY().get(i).getSecond();
}
return new Image(buffer.array(), pixelRatio, imageWrapper.id,
bitmap.getWidth(), bitmap.getHeight(), imageWrapper.sdf, arrayX, arrayY,
imageWrapper.getContent() == null ? null : imageWrapper.getContent().getContentArray()
);
}
return new Image(buffer.array(), pixelRatio, imageWrapper.id,
bitmap.getWidth(), bitmap.getHeight(), imageWrapper.sdf
);

View File

@@ -1123,6 +1123,18 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.StretchableImageActivity"
android:description="@string/description_stretchable_image"
android:exported="true"
android:label="@string/activity_stretchable_image">
<meta-data
android:name="@string/category"
android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity>
<!-- For Instrumentation tests -->
<activity
android:name=".activity.style.RuntimeStyleTimingTestActivity"

View File

@@ -0,0 +1,49 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [40, -30]
},
"properties": {
"image-name": "popup-debug",
"name": "Line 1\nLine 2\nLine 3"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [40, 30]
},
"properties": {
"image-name": "popup",
"name": "Line 1\nLine 2\nLine 3"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-40, -30]
},
"properties": {
"image-name": "popup-debug",
"name": "One longer line"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-40, 30]
},
"properties": {
"image-name": "popup",
"name": "One longer line"
}
}
]
}

View File

@@ -0,0 +1,198 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.maps.ImageContent;
import com.mapbox.mapboxsdk.maps.ImageStretches;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.GeoParseUtil;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import timber.log.Timber;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.layers.Property.ICON_TEXT_FIT_BOTH;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTextFit;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
/**
* Test stretchable image as a background for text..
*/
public class StretchableImageActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String NAME_POPUP = "popup";
private static final String NAME_POPUP_DEBUG = "popup-debug";
private static final String STRETCH_SOURCE = "STRETCH_SOURCE";
private static final String STRETCH_LAYER = "STRETCH_LAYER";
private static final String ORIGINAL_SOURCE = "ORIGINAL_SOURCE";
private static final String ORIGINAL_LAYER = "ORIGINAL_LAYER";
private MapboxMap mapboxMap;
private MapView mapView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stretchable_image);
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> {
Bitmap popup = BitmapUtils.getBitmapFromDrawable(getResources().getDrawable(R.drawable.popup));
Bitmap popupDebug = BitmapUtils.getBitmapFromDrawable(getResources().getDrawable(R.drawable.popup_debug));
// The two (blue) columns of pixels that can be stretched horizontally:
// - the pixels between x: 25 and x: 55 can be stretched
// - the pixels between x: 85 and x: 115 can be stretched.
List<ImageStretches> stretchX = new ArrayList<>();
stretchX.add(new ImageStretches(25, 55));
stretchX.add(new ImageStretches(85, 115));
// The one (red) row of pixels that can be stretched vertically:
// - the pixels between y: 25 and y: 100 can be stretched
List<ImageStretches> stretchY = new ArrayList<>();
stretchY.add(new ImageStretches(25, 100));
// This part of the image that can contain text ([x1, y1, x2, y2]):
ImageContent content = new ImageContent(25, 25, 115, 100);
style.addImage(NAME_POPUP, popup, stretchX, stretchY, content);
style.addImage(NAME_POPUP_DEBUG, popupDebug, stretchX, stretchY, content);
new LoadFeatureTask(StretchableImageActivity.this).execute();
});
}
private void onFeatureLoaded(String json) {
if (json == null) {
Timber.e("json is null.");
return;
}
Style style = mapboxMap.getStyle();
if (style != null) {
FeatureCollection featureCollection = FeatureCollection.fromJson(json);
GeoJsonSource stretchSource = new GeoJsonSource(STRETCH_SOURCE, featureCollection);
SymbolLayer stretchLayer = new SymbolLayer(STRETCH_LAYER, STRETCH_SOURCE)
.withProperties(
textField(get("name")),
iconImage(get("image-name")),
iconAllowOverlap(true),
textAllowOverlap(true),
iconTextFit(ICON_TEXT_FIT_BOTH));
// the original, unstretched image for comparison
Point point = Point.fromLngLat(-70, 0);
Feature feature = Feature.fromGeometry(point);
FeatureCollection originalCollection = FeatureCollection.fromFeature(feature);
GeoJsonSource originalSource = new GeoJsonSource(ORIGINAL_SOURCE, originalCollection);
SymbolLayer originalLayer = new SymbolLayer(ORIGINAL_LAYER, ORIGINAL_SOURCE);
style.addSource(stretchSource);
style.addSource(originalSource);
style.addLayer(stretchLayer);
style.addLayer(originalLayer);
}
}
private static class LoadFeatureTask extends AsyncTask<Void, Integer, String> {
private WeakReference<StretchableImageActivity> activity;
private LoadFeatureTask(StretchableImageActivity activity) {
this.activity = new WeakReference<>(activity);
}
@Override
protected String doInBackground(Void... params) {
StretchableImageActivity activity = this.activity.get();
if (activity != null) {
String json = null;
try {
json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "stretchable_image.geojson");
} catch (IOException exception) {
Timber.e(exception, "Could not read feature");
}
return json;
}
return null;
}
@Override
protected void onPostExecute(String json) {
super.onPostExecute(json);
StretchableImageActivity activity = this.activity.get();
if (activity != null) {
activity.onFeatureLoaded(json);
}
}
}
@Override
protected void onStart() {
super.onStart();
mapView.onStart();
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mapbox_cameraZoom="1"/>
</RelativeLayout>

View File

@@ -20,6 +20,7 @@
<string name="description_offline">Offline Map example</string>
<string name="description_region">Download an offline region.</string>
<string name="description_within">Usage of the within expression on a buffered geometry</string>
<string name="description_stretchable_image">Usage of the stretchable image as a background for text.</string>
<string name="description_update_metadata">Update metadata example</string>
<string name="description_offline_region_delete">Delete region example</string>
<string name="description_change_resources_cache_path">Change resources cache path example</string>

View File

@@ -90,4 +90,5 @@
<string name="activity_physical_circle">Physical Unit Circle</string>
<string name="activity_region">Download region</string>
<string name="activity_within">Within expression</string>
<string name="activity_stretchable_image">Stretchable Image</string>
</resources>