Render themes: Android scoped storage, zip render theme, custom resource providers (#804)
This commit is contained in:
parent
b9cbd97c40
commit
3bb8ce00c5
@ -2,13 +2,16 @@
|
||||
|
||||
## New since 0.15.0
|
||||
|
||||
- Android: scoped storage map / theme example [#804](https://github.com/mapsforge/vtm/pull/804)
|
||||
- Render theme from zip archive [#804](https://github.com/mapsforge/vtm/pull/804)
|
||||
- Render themes: custom resource providers [#804](https://github.com/mapsforge/vtm/pull/804)
|
||||
- Nautical unit adapter with feet [#803](https://github.com/mapsforge/vtm/pull/803)
|
||||
- Many other minor improvements and bug fixes
|
||||
- [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.16.0)
|
||||
|
||||
## Version 0.15.0 (2021-01-01)
|
||||
|
||||
- Android: scoped storage example [#785](https://github.com/mapsforge/vtm/pull/785)
|
||||
- Android: scoped storage map example [#785](https://github.com/mapsforge/vtm/pull/785)
|
||||
- Mapsforge: map stream support [#784](https://github.com/mapsforge/vtm/pull/784)
|
||||
- Render theme from Android content providers [#783](https://github.com/mapsforge/vtm/pull/783)
|
||||
- Render theme xml pull parser [#786](https://github.com/mapsforge/vtm/pull/786)
|
||||
|
@ -20,6 +20,9 @@
|
||||
<item
|
||||
android:id="@+id/theme_newtron"
|
||||
android:title="@string/theme_newtron" />
|
||||
<item
|
||||
android:id="@+id/theme_external_archive"
|
||||
android:title="@string/theme_external_archive" />
|
||||
<item
|
||||
android:id="@+id/theme_external"
|
||||
android:title="@string/theme_external" />
|
||||
|
@ -6,7 +6,8 @@
|
||||
<string name="theme_osmagray">Osmagray</string>
|
||||
<string name="theme_tubes">Tubes</string>
|
||||
<string name="theme_newtron">NewTron</string>
|
||||
<string name="theme_external">External theme</string>
|
||||
<string name="theme_external">External theme (Android 5)</string>
|
||||
<string name="theme_external_archive">External theme archive</string>
|
||||
<string name="styler_mode_line">Line</string>
|
||||
<string name="styler_mode_area">Area</string>
|
||||
<string name="styler_mode_outline">Outline</string>
|
||||
@ -15,6 +16,7 @@
|
||||
<string name="style_2">Hide nature</string>
|
||||
<string name="menu_gridlayer">Grid</string>
|
||||
<string name="dialog_reverse_geocoding_title">Reverse Geocoding</string>
|
||||
<string name="dialog_theme_title">Select a theme</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="error">Error</string>
|
||||
|
@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright 2014 Hannes Janetzek
|
||||
* Copyright 2016-2020 devemux86
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 Longri
|
||||
* Copyright 2018 Gustl22
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -20,13 +21,17 @@
|
||||
package org.oscim.android.test;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import org.oscim.android.theme.ContentRenderTheme;
|
||||
import org.oscim.android.theme.ContentResolverResourceProvider;
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.core.MapElement;
|
||||
import org.oscim.core.MapPosition;
|
||||
@ -42,9 +47,7 @@ import org.oscim.renderer.BitmapRenderer;
|
||||
import org.oscim.renderer.GLViewport;
|
||||
import org.oscim.renderer.bucket.RenderBuckets;
|
||||
import org.oscim.scalebar.*;
|
||||
import org.oscim.theme.IRenderTheme;
|
||||
import org.oscim.theme.ThemeFile;
|
||||
import org.oscim.theme.VtmThemes;
|
||||
import org.oscim.theme.*;
|
||||
import org.oscim.theme.styles.AreaStyle;
|
||||
import org.oscim.theme.styles.RenderStyle;
|
||||
import org.oscim.tiling.source.mapfile.MapFileTileSource;
|
||||
@ -52,15 +55,20 @@ import org.oscim.tiling.source.mapfile.MapInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class MapsforgeActivity extends MapActivity {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MapsforgeActivity.class);
|
||||
|
||||
static final int SELECT_MAP_FILE = 0;
|
||||
static final int SELECT_THEME_FILE = 1;
|
||||
private static final int SELECT_THEME_ARCHIVE = 1;
|
||||
private static final int SELECT_THEME_DIR = 2;
|
||||
static final int SELECT_THEME_FILE = 3;
|
||||
|
||||
private static final Tag ISSEA_TAG = new Tag("natural", "issea");
|
||||
private static final Tag NOSEA_TAG = new Tag("natural", "nosea");
|
||||
@ -71,6 +79,7 @@ public class MapsforgeActivity extends MapActivity {
|
||||
private final boolean mS3db;
|
||||
IRenderTheme mTheme;
|
||||
VectorTileLayer mTileLayer;
|
||||
private Uri mThemeDirUri;
|
||||
|
||||
public MapsforgeActivity() {
|
||||
this(false);
|
||||
@ -142,11 +151,19 @@ public class MapsforgeActivity extends MapActivity {
|
||||
item.setChecked(true);
|
||||
return true;
|
||||
|
||||
case R.id.theme_external:
|
||||
case R.id.theme_external_archive:
|
||||
Intent intent = new Intent(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, SELECT_THEME_FILE);
|
||||
startActivityForResult(intent, SELECT_THEME_ARCHIVE);
|
||||
return true;
|
||||
|
||||
case R.id.theme_external:
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
return false;
|
||||
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
startActivityForResult(intent, SELECT_THEME_DIR);
|
||||
return true;
|
||||
|
||||
case R.id.gridlayer:
|
||||
@ -176,75 +193,100 @@ public class MapsforgeActivity extends MapActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
MapFileTileSource tileSource = new MapFileTileSource();
|
||||
//tileSource.setPreferredLanguage("en");
|
||||
try {
|
||||
Uri uri = data.getData();
|
||||
|
||||
MapFileTileSource tileSource = new MapFileTileSource();
|
||||
//tileSource.setPreferredLanguage("en");
|
||||
FileInputStream fis = (FileInputStream) getContentResolver().openInputStream(uri);
|
||||
tileSource.setMapFileInputStream(fis);
|
||||
|
||||
mTileLayer = mMap.setBaseMap(tileSource);
|
||||
loadTheme(null);
|
||||
|
||||
if (mS3db)
|
||||
mMap.layers().add(new S3DBLayer(mMap, mTileLayer));
|
||||
else
|
||||
mMap.layers().add(new BuildingLayer(mMap, mTileLayer));
|
||||
mMap.layers().add(new LabelLayer(mMap, mTileLayer));
|
||||
|
||||
DefaultMapScaleBar mapScaleBar = new DefaultMapScaleBar(mMap);
|
||||
mapScaleBar.setScaleBarMode(DefaultMapScaleBar.ScaleBarMode.BOTH);
|
||||
mapScaleBar.setDistanceUnitAdapter(MetricUnitAdapter.INSTANCE);
|
||||
mapScaleBar.setSecondaryDistanceUnitAdapter(ImperialUnitAdapter.INSTANCE);
|
||||
mapScaleBar.setScaleBarPosition(MapScaleBar.ScaleBarPosition.BOTTOM_LEFT);
|
||||
|
||||
MapScaleBarLayer mapScaleBarLayer = new MapScaleBarLayer(mMap, mapScaleBar);
|
||||
BitmapRenderer renderer = mapScaleBarLayer.getRenderer();
|
||||
renderer.setPosition(GLViewport.Position.BOTTOM_LEFT);
|
||||
renderer.setOffset(5 * CanvasAdapter.getScale(), 0);
|
||||
mMap.layers().add(mapScaleBarLayer);
|
||||
|
||||
MapInfo info = tileSource.getMapInfo();
|
||||
if (!info.boundingBox.contains(mMap.getMapPosition().getGeoPoint())) {
|
||||
MapPosition pos = new MapPosition();
|
||||
pos.setByBoundingBox(info.boundingBox, Tile.SIZE * 4, Tile.SIZE * 4);
|
||||
mMap.setMapPosition(pos);
|
||||
mPrefs.clear();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
finish();
|
||||
}
|
||||
} else if (requestCode == SELECT_THEME_ARCHIVE) {
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
Uri uri = data.getData();
|
||||
FileInputStream fis = (FileInputStream) getContentResolver().openInputStream(uri);
|
||||
tileSource.setMapFileInputStream(fis);
|
||||
|
||||
final ZipXmlThemeResourceProvider resourceProvider = new ZipXmlThemeResourceProvider(new ZipInputStream(new BufferedInputStream(getContentResolver().openInputStream(uri))));
|
||||
final List<String> xmlThemes = resourceProvider.getXmlThemes();
|
||||
if (xmlThemes.isEmpty())
|
||||
return;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.dialog_theme_title);
|
||||
builder.setSingleChoiceItems(xmlThemes.toArray(new String[0]), -1, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
ThemeFile theme = new ZipRenderTheme(xmlThemes.get(which), resourceProvider);
|
||||
if (mTheme != null)
|
||||
mTheme.dispose();
|
||||
mTheme = mMap.setTheme(theme);
|
||||
mapsforgeTheme(mTheme);
|
||||
mMenu.findItem(R.id.theme_external_archive).setChecked(true);
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
finish();
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (requestCode == SELECT_THEME_DIR) {
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
return;
|
||||
}
|
||||
|
||||
mTileLayer = mMap.setBaseMap(tileSource);
|
||||
loadTheme(null);
|
||||
mThemeDirUri = data.getData();
|
||||
|
||||
if (mS3db)
|
||||
mMap.layers().add(new S3DBLayer(mMap, mTileLayer));
|
||||
else
|
||||
mMap.layers().add(new BuildingLayer(mMap, mTileLayer));
|
||||
mMap.layers().add(new LabelLayer(mMap, mTileLayer));
|
||||
|
||||
DefaultMapScaleBar mapScaleBar = new DefaultMapScaleBar(mMap);
|
||||
mapScaleBar.setScaleBarMode(DefaultMapScaleBar.ScaleBarMode.BOTH);
|
||||
mapScaleBar.setDistanceUnitAdapter(MetricUnitAdapter.INSTANCE);
|
||||
mapScaleBar.setSecondaryDistanceUnitAdapter(ImperialUnitAdapter.INSTANCE);
|
||||
mapScaleBar.setScaleBarPosition(MapScaleBar.ScaleBarPosition.BOTTOM_LEFT);
|
||||
|
||||
MapScaleBarLayer mapScaleBarLayer = new MapScaleBarLayer(mMap, mapScaleBar);
|
||||
BitmapRenderer renderer = mapScaleBarLayer.getRenderer();
|
||||
renderer.setPosition(GLViewport.Position.BOTTOM_LEFT);
|
||||
renderer.setOffset(5 * CanvasAdapter.getScale(), 0);
|
||||
mMap.layers().add(mapScaleBarLayer);
|
||||
|
||||
MapInfo info = tileSource.getMapInfo();
|
||||
if (!info.boundingBox.contains(mMap.getMapPosition().getGeoPoint())) {
|
||||
MapPosition pos = new MapPosition();
|
||||
pos.setByBoundingBox(info.boundingBox, Tile.SIZE * 4, Tile.SIZE * 4);
|
||||
mMap.setMapPosition(pos);
|
||||
mPrefs.clear();
|
||||
}
|
||||
// Now we have the directory for resources, but we need to let the user also select a theme file
|
||||
Intent intent = new Intent(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, mThemeDirUri);
|
||||
startActivityForResult(intent, SELECT_THEME_FILE);
|
||||
} else if (requestCode == SELECT_THEME_FILE) {
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
return;
|
||||
|
||||
Uri uri = data.getData();
|
||||
ThemeFile theme = new ContentRenderTheme(getContentResolver(), "", uri);
|
||||
|
||||
// Use tessellation with sea and land for Mapsforge themes
|
||||
if (theme.isMapsforgeTheme()) {
|
||||
mTileLayer.addHook(new VectorTileLayer.TileLoaderThemeHook() {
|
||||
@Override
|
||||
public boolean process(MapTile tile, RenderBuckets buckets, MapElement element, RenderStyle style, int level) {
|
||||
if (element.tags.contains(ISSEA_TAG) || element.tags.contains(SEA_TAG) || element.tags.contains(NOSEA_TAG)) {
|
||||
if (style instanceof AreaStyle)
|
||||
((AreaStyle) style).mesh = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(MapTile tile, boolean success) {
|
||||
}
|
||||
});
|
||||
}
|
||||
ThemeFile theme = new ContentRenderTheme(getContentResolver(), uri);
|
||||
theme.setResourceProvider(new ContentResolverResourceProvider(getContentResolver(), mThemeDirUri));
|
||||
|
||||
if (mTheme != null)
|
||||
mTheme.dispose();
|
||||
mTheme = mMap.setTheme(theme);
|
||||
mapsforgeTheme(mTheme);
|
||||
mMenu.findItem(R.id.theme_external).setChecked(true);
|
||||
}
|
||||
}
|
||||
@ -254,4 +296,25 @@ public class MapsforgeActivity extends MapActivity {
|
||||
mTheme.dispose();
|
||||
mTheme = mMap.setTheme(VtmThemes.DEFAULT);
|
||||
}
|
||||
|
||||
private void mapsforgeTheme(IRenderTheme theme) {
|
||||
if (!theme.isMapsforgeTheme())
|
||||
return;
|
||||
|
||||
// Use tessellation with sea and land for Mapsforge themes
|
||||
mTileLayer.addHook(new VectorTileLayer.TileLoaderThemeHook() {
|
||||
@Override
|
||||
public boolean process(MapTile tile, RenderBuckets buckets, MapElement element, RenderStyle style, int level) {
|
||||
if (element.tags.contains(ISSEA_TAG) || element.tags.contains(SEA_TAG) || element.tags.contains(NOSEA_TAG)) {
|
||||
if (style instanceof AreaStyle)
|
||||
((AreaStyle) style).mesh = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(MapTile tile, boolean success) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ public class Samples extends Activity {
|
||||
LinearLayout linearLayout = findViewById(R.id.samples);
|
||||
linearLayout.addView(createButton(GettingStarted.class));
|
||||
linearLayout.addView(createLabel(null));
|
||||
linearLayout.addView(createButton(SimpleMapActivity.class));
|
||||
linearLayout.addView(createButton(MapsforgeActivity.class));
|
||||
linearLayout.addView(createButton(SimpleMapActivity.class));
|
||||
linearLayout.addView(createButton(MBTilesMvtActivity.class));
|
||||
linearLayout.addView(createButton(MapilionMvtActivity.class));
|
||||
/*linearLayout.addView(createButton(MapzenMvtActivity.class));
|
||||
|
@ -30,6 +30,7 @@ import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.backend.canvas.Paint;
|
||||
import org.oscim.layers.marker.MarkerSymbol;
|
||||
import org.oscim.layers.marker.MarkerSymbol.HotspotPlace;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -69,8 +70,8 @@ public final class AndroidGraphics extends CanvasAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, int width, int height, int percent) throws IOException {
|
||||
return createBitmap(relativePathPrefix, src, width, height, percent);
|
||||
public Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) throws IOException {
|
||||
return createBitmap(relativePathPrefix, src, resourceProvider, width, height, percent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 Andrey Novikov
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -21,6 +22,7 @@ import android.text.TextUtils;
|
||||
import org.oscim.theme.IRenderTheme.ThemeException;
|
||||
import org.oscim.theme.ThemeFile;
|
||||
import org.oscim.theme.XmlRenderThemeMenuCallback;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
import org.oscim.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -38,6 +40,7 @@ public class AssetsRenderTheme implements ThemeFile {
|
||||
private boolean mMapsforgeTheme;
|
||||
private XmlRenderThemeMenuCallback mMenuCallback;
|
||||
private final String mRelativePathPrefix;
|
||||
private XmlThemeResourceProvider mResourceProvider;
|
||||
|
||||
/**
|
||||
* @param assetManager the Android asset manager.
|
||||
@ -99,6 +102,11 @@ public class AssetsRenderTheme implements ThemeFile {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlThemeResourceProvider getResourceProvider() {
|
||||
return mResourceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapsforgeTheme() {
|
||||
return mMapsforgeTheme;
|
||||
@ -113,4 +121,9 @@ public class AssetsRenderTheme implements ThemeFile {
|
||||
public void setMenuCallback(XmlRenderThemeMenuCallback menuCallback) {
|
||||
mMenuCallback = menuCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceProvider(XmlThemeResourceProvider resourceProvider) {
|
||||
mResourceProvider = resourceProvider;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2020-2021 devemux86
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -19,7 +20,7 @@ import android.net.Uri;
|
||||
import org.oscim.theme.IRenderTheme.ThemeException;
|
||||
import org.oscim.theme.ThemeFile;
|
||||
import org.oscim.theme.XmlRenderThemeMenuCallback;
|
||||
import org.oscim.utils.Utils;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -34,29 +35,26 @@ public class ContentRenderTheme implements ThemeFile {
|
||||
private final ContentResolver mContentResolver;
|
||||
private boolean mMapsforgeTheme;
|
||||
private XmlRenderThemeMenuCallback mMenuCallback;
|
||||
private final String mRelativePathPrefix;
|
||||
private XmlThemeResourceProvider mResourceProvider;
|
||||
private final Uri mUri;
|
||||
|
||||
/**
|
||||
* @param contentResolver the Android content resolver.
|
||||
* @param relativePathPrefix the prefix for all relative resource paths.
|
||||
* @param uri the XML render theme URI.
|
||||
* @param contentResolver the Android content resolver.
|
||||
* @param uri the XML render theme URI.
|
||||
* @throws ThemeException if an error occurs while reading the render theme XML.
|
||||
*/
|
||||
public ContentRenderTheme(ContentResolver contentResolver, String relativePathPrefix, Uri uri) throws ThemeException {
|
||||
this(contentResolver, relativePathPrefix, uri, null);
|
||||
public ContentRenderTheme(ContentResolver contentResolver, Uri uri) throws ThemeException {
|
||||
this(contentResolver, uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentResolver the Android content resolver.
|
||||
* @param relativePathPrefix the prefix for all relative resource paths.
|
||||
* @param uri the XML render theme URI.
|
||||
* @param menuCallback the interface callback to create a settings menu on the fly.
|
||||
* @param contentResolver the Android content resolver.
|
||||
* @param uri the XML render theme URI.
|
||||
* @param menuCallback the interface callback to create a settings menu on the fly.
|
||||
* @throws ThemeException if an error occurs while reading the render theme XML.
|
||||
*/
|
||||
public ContentRenderTheme(ContentResolver contentResolver, String relativePathPrefix, Uri uri, XmlRenderThemeMenuCallback menuCallback) throws ThemeException {
|
||||
public ContentRenderTheme(ContentResolver contentResolver, Uri uri, XmlRenderThemeMenuCallback menuCallback) throws ThemeException {
|
||||
mContentResolver = contentResolver;
|
||||
mRelativePathPrefix = relativePathPrefix;
|
||||
mUri = uri;
|
||||
mMenuCallback = menuCallback;
|
||||
}
|
||||
@ -72,9 +70,6 @@ public class ContentRenderTheme implements ThemeFile {
|
||||
if (getRenderThemeAsStream() != other.getRenderThemeAsStream()) {
|
||||
return false;
|
||||
}
|
||||
if (!Utils.equals(mRelativePathPrefix, other.mRelativePathPrefix)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -85,7 +80,7 @@ public class ContentRenderTheme implements ThemeFile {
|
||||
|
||||
@Override
|
||||
public String getRelativePathPrefix() {
|
||||
return mRelativePathPrefix;
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -97,6 +92,11 @@ public class ContentRenderTheme implements ThemeFile {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlThemeResourceProvider getResourceProvider() {
|
||||
return mResourceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapsforgeTheme() {
|
||||
return mMapsforgeTheme;
|
||||
@ -111,4 +111,9 @@ public class ContentRenderTheme implements ThemeFile {
|
||||
public void setMenuCallback(XmlRenderThemeMenuCallback menuCallback) {
|
||||
mMenuCallback = menuCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceProvider(XmlThemeResourceProvider resourceProvider) {
|
||||
mResourceProvider = resourceProvider;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.theme;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
import org.oscim.utils.IOUtils;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An xml theme resource provider resolving resources using Android scoped storage (document framework).
|
||||
* <p>
|
||||
* Implementation note: these methods do not use DocumentFile internally,
|
||||
* but query directly for document info due to vastly better performance.
|
||||
* Also for better performance, this implementation caches resource uris.
|
||||
* <p>
|
||||
* Note: this implementation requires minimum Android 5.0 (API 21)
|
||||
*/
|
||||
public class ContentResolverResourceProvider implements XmlThemeResourceProvider {
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
private final Uri relativeRootUri;
|
||||
|
||||
private final Map<String, Uri> resourceUriCache = new HashMap<>();
|
||||
|
||||
private static class DocumentInfo {
|
||||
private final String name;
|
||||
private final Uri uri;
|
||||
private final boolean isDirectory;
|
||||
|
||||
private DocumentInfo(String name, Uri uri, boolean isDirectory) {
|
||||
this.name = name;
|
||||
this.uri = uri;
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
public ContentResolverResourceProvider(ContentResolver contentResolver, Uri treeUri) {
|
||||
this.contentResolver = contentResolver;
|
||||
this.relativeRootUri = treeUri;
|
||||
|
||||
refreshCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build uri cache for one dir level (recursive function).
|
||||
*/
|
||||
private void buildCacheLevel(String prefix, Uri dirUri) {
|
||||
List<DocumentInfo> docs = queryDir(dirUri);
|
||||
for (DocumentInfo doc : docs) {
|
||||
if (doc.isDirectory) {
|
||||
buildCacheLevel(prefix + doc.name + "/", doc.uri);
|
||||
} else {
|
||||
resourceUriCache.put(prefix + doc.name, doc.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createInputStream(String relativePath, String source) throws FileNotFoundException {
|
||||
Uri docUri = resourceUriCache.get(source);
|
||||
if (docUri != null) {
|
||||
return contentResolver.openInputStream(docUri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the content of a directory using scoped storage.
|
||||
*
|
||||
* @return a list of arrays with info [0: name (String), 1: uri (Uri), 2: isDir (boolean)]
|
||||
*/
|
||||
private List<DocumentInfo> queryDir(Uri dirUri) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (dirUri == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<DocumentInfo> result = new ArrayList<>();
|
||||
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(dirUri, DocumentsContract.getDocumentId(dirUri));
|
||||
|
||||
String[] columns = new String[]{
|
||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||
};
|
||||
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = contentResolver.query(childrenUri, columns, null, null, null);
|
||||
|
||||
while (c.moveToNext()) {
|
||||
String documentId = c.getString(0);
|
||||
String name = c.getString(1);
|
||||
String mimeType = c.getString(2);
|
||||
|
||||
Uri uri = DocumentsContract.buildDocumentUriUsingTree(dirUri, documentId);
|
||||
boolean isDir = DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
|
||||
result.add(new DocumentInfo(name, uri, isDir));
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the uri cache by recreating it.
|
||||
*/
|
||||
private void refreshCache() {
|
||||
resourceUriCache.clear();
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
if (relativeRootUri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uri dirUri = DocumentsContract.buildDocumentUriUsingTree(relativeRootUri, DocumentsContract.getTreeDocumentId(relativeRootUri));
|
||||
buildCacheLevel(CanvasAdapter.PREFIX_FILE, dirUri);
|
||||
}
|
||||
}
|
@ -23,11 +23,9 @@ import org.oscim.backend.Platform;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.backend.canvas.Paint;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -119,7 +117,7 @@ public class AwtGraphics extends CanvasAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, int width, int height, int percent) throws IOException {
|
||||
return createBitmap(relativePathPrefix, src, width, height, percent);
|
||||
public Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) throws IOException {
|
||||
return createBitmap(relativePathPrefix, src, resourceProvider, width, height, percent);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
package org.oscim.ios.test;
|
||||
|
||||
import com.badlogic.gdx.graphics.glutils.GLVersion;
|
||||
|
||||
import org.oscim.backend.GLAdapter;
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.GeoPoint;
|
||||
@ -74,7 +73,7 @@ public class IOSPathLayerTest extends GdxMap {
|
||||
|
||||
mMap.setMapPosition(0, 0, 1 << 2);
|
||||
|
||||
tex = Utils.loadTexture("", "patterns/pike.png", 0, 0, 100);
|
||||
tex = Utils.loadTexture("", "patterns/pike.png", null, 0, 0, 100);
|
||||
// tex = new TextureItem(CanvasAdapter.getBitmapAsset("", "patterns/pike.png"));
|
||||
tex.mipmap = true;
|
||||
|
||||
|
@ -21,6 +21,7 @@ import org.oscim.backend.Platform;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.backend.canvas.Paint;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -70,7 +71,7 @@ public class IosGraphics extends CanvasAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, int width, int height, int percent) throws IOException {
|
||||
return createBitmap(relativePathPrefix, src, width, height, percent);
|
||||
protected Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) throws IOException {
|
||||
return createBitmap(relativePathPrefix, src, resourceProvider, width, height, percent);
|
||||
}
|
||||
}
|
||||
|
@ -13,4 +13,5 @@ dependencies {
|
||||
sourceSets {
|
||||
main.java.srcDirs = ['src']
|
||||
test.java.srcDirs = ['test']
|
||||
test.resources.srcDirs = ['resources']
|
||||
}
|
||||
|
BIN
vtm-tests/resources/xmlthemetest.zip
Normal file
BIN
vtm-tests/resources/xmlthemetest.zip
Normal file
Binary file not shown.
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2021 eddiemuc
|
||||
* Copyright 2021 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.theme;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.oscim.utils.IOUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class ZipXmlThemeResourceProviderTest {
|
||||
|
||||
@Test
|
||||
public void openZip() throws IOException {
|
||||
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(ZipXmlThemeResourceProviderTest.class.getResourceAsStream("/xmlthemetest.zip")));
|
||||
Assert.assertNotNull(zis);
|
||||
|
||||
ZipXmlThemeResourceProvider zts = new ZipXmlThemeResourceProvider(zis);
|
||||
|
||||
// All files contained
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:one.xml"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:two.xml"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:res/three.xml"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:res/blue_star_1.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:res/test.txt"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:res/sub/four.xml"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:res/sub/blue_star_sub_1.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:res/sub/blue_star_sub_2.svg"));
|
||||
|
||||
//Relative Reference ok
|
||||
Assert.assertNotNull(zts.createInputStream("", "file:res/sub/blue_star_sub_2.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream("res", "file:sub/blue_star_sub_2.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream("/", "file:res/sub/blue_star_sub_2.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream("/res", "file:sub/blue_star_sub_2.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream("res/", "file:/sub/blue_star_sub_2.svg"));
|
||||
|
||||
// Can get same files using various other formats
|
||||
Assert.assertNotNull(zts.createInputStream(null, "res/sub/blue_star_sub_2.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "/res/sub/blue_star_sub_2.svg"));
|
||||
Assert.assertNotNull(zts.createInputStream(null, "file:/res/sub/blue_star_sub_2.svg"));
|
||||
|
||||
// Dirs NOT contained!
|
||||
Assert.assertNull(zts.createInputStream(null, "file:res/"));
|
||||
|
||||
Assert.assertEquals(8, zts.getCount());
|
||||
|
||||
List<String> xmlThemes = zts.getXmlThemes();
|
||||
Assert.assertEquals(4, xmlThemes.size());
|
||||
Assert.assertTrue(xmlThemes.contains("one.xml"));
|
||||
Assert.assertTrue(xmlThemes.contains("two.xml"));
|
||||
Assert.assertTrue(xmlThemes.contains("res/three.xml"));
|
||||
Assert.assertTrue(xmlThemes.contains("res/sub/four.xml"));
|
||||
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(zts.createInputStream(null, "file:res/test.txt")));
|
||||
String line = reader.readLine();
|
||||
Assert.assertEquals(line, "This is a test");
|
||||
} finally {
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openEmpty() throws IOException {
|
||||
Assert.assertTrue(new ZipXmlThemeResourceProvider(null).getXmlThemes().isEmpty());
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 Andrey Novikov
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -59,6 +60,11 @@ public enum VtmThemes implements ThemeFile {
|
||||
return AssetAdapter.readFileAsStream(mPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlThemeResourceProvider getResourceProvider() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapsforgeTheme() {
|
||||
return false;
|
||||
@ -71,4 +77,8 @@ public enum VtmThemes implements ThemeFile {
|
||||
@Override
|
||||
public void setMenuCallback(XmlRenderThemeMenuCallback menuCallback) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceProvider(XmlThemeResourceProvider resourceProvider) {
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,11 @@ package org.oscim.gdx.client;
|
||||
import com.google.gwt.canvas.client.Canvas;
|
||||
import com.google.gwt.canvas.dom.client.Context2d;
|
||||
import com.google.gwt.canvas.dom.client.TextMetrics;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.backend.Platform;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Paint;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
@ -68,7 +68,7 @@ public class GwtGdxGraphics extends CanvasAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, int width, int height, int percent) {
|
||||
public Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) {
|
||||
String pathName = (relativePathPrefix == null || relativePathPrefix.length() == 0 ? "" : relativePathPrefix + File.separatorChar) + src;
|
||||
return new GwtBitmap(pathName);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016-2020 devemux86
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 Longri
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -21,6 +22,7 @@ package org.oscim.backend;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.backend.canvas.Paint;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -37,7 +39,7 @@ public abstract class CanvasAdapter {
|
||||
private static final Logger log = LoggerFactory.getLogger(CanvasAdapter.class);
|
||||
|
||||
private static final String PREFIX_ASSETS = "assets:";
|
||||
private static final String PREFIX_FILE = "file:";
|
||||
public static final String PREFIX_FILE = "file:";
|
||||
|
||||
/**
|
||||
* The instance provided by backend
|
||||
@ -156,34 +158,45 @@ public abstract class CanvasAdapter {
|
||||
* @param src the resource
|
||||
* @return the bitmap
|
||||
*/
|
||||
protected abstract Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, int width, int height, int percent) throws IOException;
|
||||
protected abstract Bitmap loadBitmapAssetImpl(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) throws IOException;
|
||||
|
||||
public static Bitmap getBitmapAsset(String relativePathPrefix, String src) throws IOException {
|
||||
return getBitmapAsset(relativePathPrefix, src, 0, 0, 100);
|
||||
return getBitmapAsset(relativePathPrefix, src, null, 0, 0, 100);
|
||||
}
|
||||
|
||||
public static Bitmap getBitmapAsset(String relativePathPrefix, String src, int width, int height, int percent) throws IOException {
|
||||
return g.loadBitmapAssetImpl(relativePathPrefix, src, width, height, percent);
|
||||
public static Bitmap getBitmapAsset(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) throws IOException {
|
||||
return g.loadBitmapAssetImpl(relativePathPrefix, src, resourceProvider, width, height, percent);
|
||||
}
|
||||
|
||||
protected static Bitmap createBitmap(String relativePathPrefix, String src, int width, int height, int percent) throws IOException {
|
||||
protected static Bitmap createBitmap(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) throws IOException {
|
||||
if (src == null || src.length() == 0) {
|
||||
// no image source defined
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream inputStream;
|
||||
if (src.startsWith(PREFIX_ASSETS)) {
|
||||
src = src.substring(PREFIX_ASSETS.length());
|
||||
inputStream = inputStreamFromAssets(relativePathPrefix, src);
|
||||
} else if (src.startsWith(PREFIX_FILE)) {
|
||||
src = src.substring(PREFIX_FILE.length());
|
||||
inputStream = inputStreamFromFile(relativePathPrefix, src);
|
||||
} else {
|
||||
inputStream = inputStreamFromFile(relativePathPrefix, src);
|
||||
InputStream inputStream = null;
|
||||
if (resourceProvider != null) {
|
||||
try {
|
||||
inputStream = resourceProvider.createInputStream(relativePathPrefix, src);
|
||||
} catch (IOException ioe) {
|
||||
log.debug("Exception trying to access resource: " + src + " using custom provider: " + ioe);
|
||||
// Ignore and try to resolve input stream using the standard process
|
||||
}
|
||||
}
|
||||
|
||||
if (inputStream == null)
|
||||
if (inputStream == null) {
|
||||
if (src.startsWith(PREFIX_ASSETS)) {
|
||||
src = src.substring(PREFIX_ASSETS.length());
|
||||
inputStream = inputStreamFromAssets(relativePathPrefix, src);
|
||||
} else if (src.startsWith(PREFIX_FILE)) {
|
||||
src = src.substring(PREFIX_FILE.length());
|
||||
inputStream = inputStreamFromFile(relativePathPrefix, src);
|
||||
} else {
|
||||
inputStream = inputStreamFromFile(relativePathPrefix, src);
|
||||
|
||||
if (inputStream == null)
|
||||
inputStream = inputStreamFromAssets(relativePathPrefix, src);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to internal resources
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 Andrey Novikov
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -37,6 +38,7 @@ public class ExternalRenderTheme implements ThemeFile {
|
||||
private boolean mMapsforgeTheme;
|
||||
private XmlRenderThemeMenuCallback mMenuCallback;
|
||||
private final String mPath;
|
||||
private XmlThemeResourceProvider mResourceProvider;
|
||||
|
||||
/**
|
||||
* @param fileName the path to the XML render theme file.
|
||||
@ -109,6 +111,11 @@ public class ExternalRenderTheme implements ThemeFile {
|
||||
return is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlThemeResourceProvider getResourceProvider() {
|
||||
return mResourceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapsforgeTheme() {
|
||||
return mMapsforgeTheme;
|
||||
@ -123,4 +130,9 @@ public class ExternalRenderTheme implements ThemeFile {
|
||||
public void setMenuCallback(XmlRenderThemeMenuCallback menuCallback) {
|
||||
mMenuCallback = menuCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceProvider(XmlThemeResourceProvider resourceProvider) {
|
||||
mResourceProvider = resourceProvider;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 Andrey Novikov
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -31,12 +32,14 @@ public class StreamRenderTheme implements ThemeFile {
|
||||
private boolean mMapsforgeTheme;
|
||||
private XmlRenderThemeMenuCallback mMenuCallback;
|
||||
private final String mRelativePathPrefix;
|
||||
private XmlThemeResourceProvider mResourceProvider;
|
||||
|
||||
/**
|
||||
* @param relativePathPrefix the prefix for all relative resource paths.
|
||||
* @param inputStream an input stream containing valid render theme XML data.
|
||||
* @throws ThemeException if an error occurs while reading the render theme XML.
|
||||
*/
|
||||
public StreamRenderTheme(String relativePathPrefix, InputStream inputStream) {
|
||||
public StreamRenderTheme(String relativePathPrefix, InputStream inputStream) throws ThemeException {
|
||||
this(relativePathPrefix, inputStream, null);
|
||||
}
|
||||
|
||||
@ -44,8 +47,9 @@ public class StreamRenderTheme implements ThemeFile {
|
||||
* @param relativePathPrefix the prefix for all relative resource paths.
|
||||
* @param inputStream an input stream containing valid render theme XML data.
|
||||
* @param menuCallback the interface callback to create a settings menu on the fly.
|
||||
* @throws ThemeException if an error occurs while reading the render theme XML.
|
||||
*/
|
||||
public StreamRenderTheme(String relativePathPrefix, InputStream inputStream, XmlRenderThemeMenuCallback menuCallback) {
|
||||
public StreamRenderTheme(String relativePathPrefix, InputStream inputStream, XmlRenderThemeMenuCallback menuCallback) throws ThemeException {
|
||||
mRelativePathPrefix = relativePathPrefix;
|
||||
mInputStream = inputStream;
|
||||
mMenuCallback = menuCallback;
|
||||
@ -83,6 +87,11 @@ public class StreamRenderTheme implements ThemeFile {
|
||||
return mInputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlThemeResourceProvider getResourceProvider() {
|
||||
return mResourceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapsforgeTheme() {
|
||||
return mMapsforgeTheme;
|
||||
@ -97,4 +106,9 @@ public class StreamRenderTheme implements ThemeFile {
|
||||
public void setMenuCallback(XmlRenderThemeMenuCallback menuCallback) {
|
||||
mMenuCallback = menuCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceProvider(XmlThemeResourceProvider resourceProvider) {
|
||||
mResourceProvider = resourceProvider;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016-2021 devemux86
|
||||
* Copyright 2017 Andrey Novikov
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -44,6 +45,11 @@ public interface ThemeFile extends Serializable {
|
||||
*/
|
||||
InputStream getRenderThemeAsStream() throws ThemeException;
|
||||
|
||||
/**
|
||||
* @return a custom provider to retrieve resources internally referenced by "src" attribute (e.g. images, icons).
|
||||
*/
|
||||
XmlThemeResourceProvider getResourceProvider();
|
||||
|
||||
/**
|
||||
* Tells ThemeLoader if theme file is in Mapsforge format
|
||||
*
|
||||
@ -60,4 +66,9 @@ public interface ThemeFile extends Serializable {
|
||||
* @param menuCallback the interface callback to create a settings menu on the fly.
|
||||
*/
|
||||
void setMenuCallback(XmlRenderThemeMenuCallback menuCallback);
|
||||
|
||||
/**
|
||||
* @param resourceProvider a custom provider to retrieve resources internally referenced by "src" attribute (e.g. images, icons).
|
||||
*/
|
||||
void setResourceProvider(XmlThemeResourceProvider resourceProvider);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Copyright 2018-2019 Gustl22
|
||||
* Copyright 2018 Izumi Kawashima
|
||||
* Copyright 2019 Murray Hughes
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -664,7 +665,7 @@ public class XmlThemeBuilder {
|
||||
} else {
|
||||
if (src != null) {
|
||||
float symbolScale = Parameters.SYMBOL_SCALING == Parameters.SymbolScaling.ALL ? CanvasAdapter.symbolScale : 1;
|
||||
b.texture = Utils.loadTexture(mTheme.getRelativePathPrefix(), src, b.symbolWidth, b.symbolHeight, (int) (b.symbolPercent * symbolScale));
|
||||
b.texture = Utils.loadTexture(mTheme.getRelativePathPrefix(), src, mTheme.getResourceProvider(), b.symbolWidth, b.symbolHeight, (int) (b.symbolPercent * symbolScale));
|
||||
}
|
||||
|
||||
if (b.texture != null && hasSymbol) {
|
||||
@ -777,7 +778,7 @@ public class XmlThemeBuilder {
|
||||
}
|
||||
|
||||
if (src != null)
|
||||
b.texture = Utils.loadTexture(mTheme.getRelativePathPrefix(), src, b.symbolWidth, b.symbolHeight, b.symbolPercent);
|
||||
b.texture = Utils.loadTexture(mTheme.getRelativePathPrefix(), src, mTheme.getResourceProvider(), b.symbolWidth, b.symbolHeight, b.symbolPercent);
|
||||
|
||||
return b.build();
|
||||
}
|
||||
@ -1103,7 +1104,7 @@ public class XmlThemeBuilder {
|
||||
String lowValue = symbol.toLowerCase(Locale.ENGLISH);
|
||||
if (lowValue.endsWith(".png") || lowValue.endsWith(".svg")) {
|
||||
try {
|
||||
b.bitmap = CanvasAdapter.getBitmapAsset(mTheme.getRelativePathPrefix(), symbol, b.symbolWidth, b.symbolHeight, (int) (b.symbolPercent * CanvasAdapter.symbolScale));
|
||||
b.bitmap = CanvasAdapter.getBitmapAsset(mTheme.getRelativePathPrefix(), symbol, mTheme.getResourceProvider(), b.symbolWidth, b.symbolHeight, (int) (b.symbolPercent * CanvasAdapter.symbolScale));
|
||||
} catch (Exception e) {
|
||||
log.error("{}: {}", symbol, e.getMessage());
|
||||
}
|
||||
@ -1257,7 +1258,7 @@ public class XmlThemeBuilder {
|
||||
symbolScale = CanvasAdapter.symbolScale;
|
||||
break;
|
||||
}
|
||||
Bitmap bitmap = CanvasAdapter.getBitmapAsset(mTheme.getRelativePathPrefix(), b.src, b.symbolWidth, b.symbolHeight, (int) (b.symbolPercent * symbolScale));
|
||||
Bitmap bitmap = CanvasAdapter.getBitmapAsset(mTheme.getRelativePathPrefix(), b.src, mTheme.getResourceProvider(), b.symbolWidth, b.symbolHeight, (int) (b.symbolPercent * symbolScale));
|
||||
if (bitmap != null)
|
||||
return buildSymbol(b, b.src, bitmap);
|
||||
} catch (Exception e) {
|
||||
|
32
vtm/src/org/oscim/theme/XmlThemeResourceProvider.java
Normal file
32
vtm/src/org/oscim/theme/XmlThemeResourceProvider.java
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.theme;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Interface for a provider of resources referenced inside XML themes.
|
||||
*/
|
||||
public interface XmlThemeResourceProvider {
|
||||
|
||||
/**
|
||||
* @param relativePath a relative path to use as a base for search in the resource provuider
|
||||
* @param source a source string parsed out of an XML render theme "src" attribute.
|
||||
* @return an InputStream to read the resource data from.
|
||||
* @throws IOException if the resource cannot be found or an access error occurred.
|
||||
*/
|
||||
InputStream createInputStream(String relativePath, String source) throws IOException;
|
||||
}
|
120
vtm/src/org/oscim/theme/ZipRenderTheme.java
Normal file
120
vtm/src/org/oscim/theme/ZipRenderTheme.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2021 devemux86
|
||||
* Copyright 2021 eddiemuc
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.theme;
|
||||
|
||||
import org.oscim.theme.IRenderTheme.ThemeException;
|
||||
import org.oscim.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A ZipRenderTheme allows for customizing the rendering style of the map
|
||||
* via an XML from an archive.
|
||||
*/
|
||||
public class ZipRenderTheme implements ThemeFile {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean mMapsforgeTheme;
|
||||
private XmlRenderThemeMenuCallback mMenuCallback;
|
||||
private final String mRelativePathPrefix;
|
||||
private XmlThemeResourceProvider mResourceProvider;
|
||||
protected final String mXmlTheme;
|
||||
|
||||
/**
|
||||
* @param xmlTheme the XML theme path in the archive.
|
||||
* @param resourceProvider the custom provider to retrieve resources internally referenced by "src" attribute (e.g. images, icons).
|
||||
* @throws ThemeException if an error occurs while reading the render theme XML.
|
||||
*/
|
||||
public ZipRenderTheme(String xmlTheme, XmlThemeResourceProvider resourceProvider) throws ThemeException {
|
||||
this(xmlTheme, resourceProvider, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param xmlTheme the XML theme path in the archive.
|
||||
* @param resourceProvider the custom provider to retrieve resources internally referenced by "src" attribute (e.g. images, icons).
|
||||
* @param menuCallback the interface callback to create a settings menu on the fly.
|
||||
* @throws ThemeException if an error occurs while reading the render theme XML.
|
||||
*/
|
||||
public ZipRenderTheme(String xmlTheme, XmlThemeResourceProvider resourceProvider, XmlRenderThemeMenuCallback menuCallback) throws ThemeException {
|
||||
mXmlTheme = xmlTheme;
|
||||
mResourceProvider = resourceProvider;
|
||||
mMenuCallback = menuCallback;
|
||||
|
||||
mRelativePathPrefix = xmlTheme.substring(0, xmlTheme.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (!(obj instanceof ZipRenderTheme)) {
|
||||
return false;
|
||||
}
|
||||
ZipRenderTheme other = (ZipRenderTheme) obj;
|
||||
if (getRenderThemeAsStream() != other.getRenderThemeAsStream()) {
|
||||
return false;
|
||||
}
|
||||
if (!Utils.equals(mRelativePathPrefix, other.mRelativePathPrefix)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlRenderThemeMenuCallback getMenuCallback() {
|
||||
return mMenuCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePathPrefix() {
|
||||
return mRelativePathPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getRenderThemeAsStream() throws ThemeException {
|
||||
try {
|
||||
return mResourceProvider.createInputStream(mRelativePathPrefix, mXmlTheme.substring(mXmlTheme.lastIndexOf("/") + 1));
|
||||
} catch (IOException e) {
|
||||
throw new ThemeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlThemeResourceProvider getResourceProvider() {
|
||||
return mResourceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapsforgeTheme() {
|
||||
return mMapsforgeTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMapsforgeTheme(boolean mapsforgeTheme) {
|
||||
mMapsforgeTheme = mapsforgeTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenuCallback(XmlRenderThemeMenuCallback menuCallback) {
|
||||
mMenuCallback = menuCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceProvider(XmlThemeResourceProvider resourceProvider) {
|
||||
mResourceProvider = resourceProvider;
|
||||
}
|
||||
}
|
124
vtm/src/org/oscim/theme/ZipXmlThemeResourceProvider.java
Normal file
124
vtm/src/org/oscim/theme/ZipXmlThemeResourceProvider.java
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2021 eddiemuc
|
||||
* Copyright 2021 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.theme;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.utils.IOUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* Resource provider reading resource files out of a zip input stream.
|
||||
* <p>
|
||||
* Resources are cached.
|
||||
*/
|
||||
public class ZipXmlThemeResourceProvider implements XmlThemeResourceProvider {
|
||||
|
||||
private final Map<String, byte[]> files = new HashMap<>();
|
||||
private final List<String> xmlThemes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* @param zipInputStream zip stream to read resources from
|
||||
* @throws IOException if a problem occurs reading the stream
|
||||
*/
|
||||
public ZipXmlThemeResourceProvider(ZipInputStream zipInputStream) throws IOException {
|
||||
this(zipInputStream, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zipInputStream zip stream to read resources from
|
||||
* @param maxResourceSizeToCache only resources in the zip stream with a maximum size of this parameter (in bytes) are cached and provided
|
||||
* @throws IOException if a problem occurs reading the stream
|
||||
*/
|
||||
public ZipXmlThemeResourceProvider(ZipInputStream zipInputStream, int maxResourceSizeToCache) throws IOException {
|
||||
if (zipInputStream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (zipEntry.isDirectory() || zipEntry.getSize() > maxResourceSizeToCache) {
|
||||
continue;
|
||||
}
|
||||
byte[] entry = streamToBytes(zipInputStream, (int) zipEntry.getSize());
|
||||
String fileName = zipEntry.getName();
|
||||
if (fileName.startsWith("/")) {
|
||||
fileName = fileName.substring(1);
|
||||
}
|
||||
files.put(fileName, entry);
|
||||
if (fileName.toLowerCase(Locale.ROOT).endsWith(".xml")) {
|
||||
xmlThemes.add(fileName);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(zipInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createInputStream(String relativePath, String source) {
|
||||
String sourceKey = source;
|
||||
if (sourceKey.startsWith(CanvasAdapter.PREFIX_FILE)) {
|
||||
sourceKey = sourceKey.substring(CanvasAdapter.PREFIX_FILE.length());
|
||||
}
|
||||
if (sourceKey.startsWith("/")) {
|
||||
sourceKey = sourceKey.substring(1);
|
||||
}
|
||||
if (relativePath != null) {
|
||||
if (relativePath.startsWith("/")) {
|
||||
relativePath = relativePath.substring(1);
|
||||
}
|
||||
if (relativePath.endsWith("/")) {
|
||||
relativePath = relativePath.substring(0, relativePath.length() - 1);
|
||||
}
|
||||
sourceKey = relativePath.isEmpty() ? sourceKey : relativePath + "/" + sourceKey;
|
||||
}
|
||||
if (files.containsKey(sourceKey)) {
|
||||
return new ByteArrayInputStream(files.get(sourceKey));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of files in the archive.
|
||||
*/
|
||||
public int getCount() {
|
||||
return files.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the XML theme paths in the archive.
|
||||
*/
|
||||
public List<String> getXmlThemes() {
|
||||
return xmlThemes;
|
||||
}
|
||||
|
||||
private static byte[] streamToBytes(InputStream in, int size) throws IOException {
|
||||
byte[] bytes = new byte[size];
|
||||
int count, offset = 0;
|
||||
while ((count = in.read(bytes, offset, size)) > 0) {
|
||||
size -= count;
|
||||
offset += count;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.renderer.bucket.TextureItem;
|
||||
import org.oscim.theme.XmlThemeResourceProvider;
|
||||
import org.oscim.utils.math.MathUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -37,12 +38,12 @@ public final class Utils {
|
||||
/**
|
||||
* Load a texture from a specified location and optional dimensions.
|
||||
*/
|
||||
public static TextureItem loadTexture(String relativePathPrefix, String src, int width, int height, int percent) {
|
||||
public static TextureItem loadTexture(String relativePathPrefix, String src, XmlThemeResourceProvider resourceProvider, int width, int height, int percent) {
|
||||
if (src == null || src.length() == 0)
|
||||
return null;
|
||||
|
||||
try {
|
||||
Bitmap bitmap = CanvasAdapter.getBitmapAsset(relativePathPrefix, src, width, height, percent);
|
||||
Bitmap bitmap = CanvasAdapter.getBitmapAsset(relativePathPrefix, src, resourceProvider, width, height, percent);
|
||||
if (bitmap != null) {
|
||||
log.debug("loading {}", src);
|
||||
return new TextureItem(potBitmap(bitmap), true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user