Mapsforge themes parser united with VTM #100

This commit is contained in:
Emux 2018-07-05 21:42:56 +03:00
parent 40f2910991
commit f2bf6cd6ee
No known key found for this signature in database
GPG Key ID: 64ED9980896038C3
6 changed files with 73 additions and 1405 deletions

View File

@ -1,6 +1,6 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2016-2017 devemux86
* Copyright 2016-2018 devemux86
* Copyright 2017 Longri
*
* This program is free software: you can redistribute it and/or modify it under the
@ -18,8 +18,6 @@ package org.oscim.android.filepicker;
import org.oscim.theme.ExternalRenderTheme;
import org.oscim.theme.ThemeFile;
import org.oscim.theme.ThemeUtils;
import org.oscim.theme.XmlMapsforgeThemeBuilder;
import org.oscim.theme.XmlThemeBuilder;
import org.oscim.tiling.TileSource.OpenResult;
import org.xml.sax.InputSource;
@ -41,11 +39,7 @@ public final class ValidRenderTheme implements ValidFileFilter {
try {
ThemeFile theme = new ExternalRenderTheme(file.getAbsolutePath());
DefaultHandler renderThemeHandler;
if (ThemeUtils.isMapsforgeTheme(theme))
renderThemeHandler = new XmlMapsforgeThemeBuilder(theme);
else
renderThemeHandler = new XmlThemeBuilder(theme);
DefaultHandler renderThemeHandler = new XmlThemeBuilder(theme);
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
xmlReader.setContentHandler(renderThemeHandler);
xmlReader.parse(new InputSource(theme.getRenderThemeAsStream()));

View File

@ -1,6 +1,6 @@
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2016-2017 devemux86
* Copyright 2016-2018 devemux86
* Copyright 2017 Longri
* Copyright 2017 Andrey Novikov
*
@ -46,11 +46,7 @@ public class ThemeLoader {
}
public static IRenderTheme load(ThemeFile theme, ThemeCallback themeCallback) throws ThemeException {
IRenderTheme t;
if (theme.isMapsforgeTheme())
t = Parameters.TEXTURE_ATLAS ? XmlMapsforgeAtlasThemeBuilder.read(theme, themeCallback) : XmlMapsforgeThemeBuilder.read(theme, themeCallback);
else
t = Parameters.TEXTURE_ATLAS ? XmlAtlasThemeBuilder.read(theme, themeCallback) : XmlThemeBuilder.read(theme, themeCallback);
IRenderTheme t = Parameters.TEXTURE_ATLAS ? XmlAtlasThemeBuilder.read(theme, themeCallback) : XmlThemeBuilder.read(theme, themeCallback);
if (t != null)
t.scaleTextSize(CanvasAdapter.getScale() * CanvasAdapter.textScale);
return t;

View File

@ -112,7 +112,7 @@ public class XmlAtlasThemeBuilder extends XmlThemeBuilder {
@Override
RenderTheme createTheme(Rule[] rules) {
return new AtlasRenderTheme(mMapBackground, mTextScale, rules, mLevels, regionMap, atlasList);
return new AtlasRenderTheme(mMapBackground, mTextScale, rules, mLevels, mMapsforgeTheme, regionMap, atlasList);
}
@Override

View File

@ -1,127 +0,0 @@
/*
* Copyright 2017 Longri
* Copyright 2017-2018 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.backend.Platform;
import org.oscim.backend.XMLReaderAdapter;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.renderer.atlas.TextureAtlas;
import org.oscim.renderer.atlas.TextureRegion;
import org.oscim.theme.IRenderTheme.ThemeException;
import org.oscim.theme.rule.Rule;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.theme.styles.SymbolStyle;
import org.oscim.theme.styles.SymbolStyle.SymbolBuilder;
import org.oscim.utils.TextureAtlasUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class XmlMapsforgeAtlasThemeBuilder extends XmlMapsforgeThemeBuilder {
/**
* @param theme an input theme containing valid render theme XML data.
* @return a new RenderTheme which is created by parsing the XML data from the input theme.
* @throws ThemeException if an error occurs while parsing the render theme XML.
*/
public static IRenderTheme read(ThemeFile theme) throws ThemeException {
return read(theme, null);
}
/**
* @param theme an input theme containing valid render theme XML data.
* @param themeCallback the theme callback.
* @return a new RenderTheme which is created by parsing the XML data from the input theme.
* @throws ThemeException if an error occurs while parsing the render theme XML.
*/
public static IRenderTheme read(ThemeFile theme, ThemeCallback themeCallback) throws ThemeException {
Map<Object, TextureRegion> outputMap = new HashMap<>();
List<TextureAtlas> atlasList = new ArrayList<>();
XmlMapsforgeAtlasThemeBuilder renderThemeHandler = new XmlMapsforgeAtlasThemeBuilder(theme, themeCallback, outputMap, atlasList);
try {
new XMLReaderAdapter().parse(renderThemeHandler, theme.getRenderThemeAsStream());
} catch (Exception e) {
throw new ThemeException(e.getMessage());
}
TextureAtlasUtils.createTextureRegions(renderThemeHandler.bitmapMap, outputMap, atlasList,
true, CanvasAdapter.platform == Platform.IOS);
return replaceThemeSymbols(renderThemeHandler.mRenderTheme, outputMap);
}
private static IRenderTheme replaceThemeSymbols(RenderTheme renderTheme, Map<Object, TextureRegion> regionMap) {
SymbolBuilder<?> symbolBuilder = SymbolStyle.builder();
for (Rule rule : renderTheme.getRules()) {
replaceRuleSymbols(rule, regionMap, symbolBuilder);
}
return renderTheme;
}
private static void replaceRuleSymbols(Rule rule, Map<Object, TextureRegion> regionMap, SymbolBuilder<?> b) {
for (int i = 0, n = rule.styles.length; i < n; i++) {
RenderStyle style = rule.styles[i];
if (style instanceof SymbolStyle) {
SymbolStyle symbol = (SymbolStyle) style;
TextureRegion region = regionMap.get(symbol.hash);
if (region != null)
rule.styles[i] = b.set(symbol)
.bitmap(null)
.texture(region)
.build();
}
}
for (Rule subRule : rule.subRules) {
replaceRuleSymbols(subRule, regionMap, b);
}
}
private final Map<Object, TextureRegion> regionMap;
private final List<TextureAtlas> atlasList;
private final Map<Object, Bitmap> bitmapMap = new HashMap<>();
public XmlMapsforgeAtlasThemeBuilder(ThemeFile theme,
Map<Object, TextureRegion> regionMap, List<TextureAtlas> atlasList) {
this(theme, null, regionMap, atlasList);
}
public XmlMapsforgeAtlasThemeBuilder(ThemeFile theme, ThemeCallback themeCallback,
Map<Object, TextureRegion> regionMap, List<TextureAtlas> atlasList) {
super(theme, themeCallback);
this.regionMap = regionMap;
this.atlasList = atlasList;
}
@Override
RenderTheme createTheme(Rule[] rules) {
return new AtlasRenderTheme(mMapBackground, mTextScale, rules, mLevels, true, regionMap, atlasList);
}
@Override
SymbolStyle buildSymbol(SymbolBuilder<?> b, String src, Bitmap bitmap) {
// we need to hash with the width/height included as the same symbol could be required
// in a different size and must be cached with a size-specific hash
String absoluteName = CanvasAdapter.getAbsoluteFile(mTheme.getRelativePathPrefix(), src).getAbsolutePath();
int hash = new StringBuilder().append(absoluteName).append(b.symbolWidth).append(b.symbolHeight).append(b.symbolPercent).toString().hashCode();
bitmapMap.put(hash, bitmap);
return b.hash(hash).build();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@ import org.oscim.backend.canvas.Color;
import org.oscim.backend.canvas.Paint.Cap;
import org.oscim.backend.canvas.Paint.FontFamily;
import org.oscim.backend.canvas.Paint.FontStyle;
import org.oscim.core.Tag;
import org.oscim.renderer.atlas.TextureAtlas;
import org.oscim.renderer.atlas.TextureAtlas.Rect;
import org.oscim.renderer.atlas.TextureRegion;
@ -50,6 +51,7 @@ import org.oscim.theme.styles.SymbolStyle;
import org.oscim.theme.styles.SymbolStyle.SymbolBuilder;
import org.oscim.theme.styles.TextStyle;
import org.oscim.theme.styles.TextStyle.TextBuilder;
import org.oscim.utils.FastMath;
import org.oscim.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -73,7 +75,8 @@ public class XmlThemeBuilder extends DefaultHandler {
private static final Logger log = LoggerFactory.getLogger(XmlThemeBuilder.class);
private static final int RENDER_THEME_VERSION = 1;
private static final int RENDER_THEME_VERSION_MAPSFORGE = 6;
private static final int RENDER_THEME_VERSION_VTM = 1;
private enum Element {
RENDER_THEME, RENDERING_INSTRUCTION, RULE, STYLE, ATLAS, RENDERING_STYLE
@ -81,13 +84,16 @@ public class XmlThemeBuilder extends DefaultHandler {
private static final String ELEMENT_NAME_RENDER_THEME = "rendertheme";
private static final String ELEMENT_NAME_STYLE_MENU = "stylemenu";
private static final String ELEMENT_NAME_MATCH = "m";
private static final String ELEMENT_NAME_MATCH_MAPSFORGE = "rule";
private static final String ELEMENT_NAME_MATCH_VTM = "m";
private static final String UNEXPECTED_ELEMENT = "unexpected element: ";
private static final String LINE_STYLE = "L";
private static final String OUTLINE_STYLE = "O";
private static final String AREA_STYLE = "A";
private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE / 2;
/**
* @param theme an input theme containing valid render theme XML data.
* @return a new RenderTheme which is created by parsing the XML data from the input theme.
@ -155,6 +161,7 @@ public class XmlThemeBuilder extends DefaultHandler {
private final ThemeCallback mThemeCallback;
RenderTheme mRenderTheme;
final boolean mMapsforgeTheme;
private final float mScale, mScale2;
private Set<String> mCategories;
@ -168,15 +175,21 @@ public class XmlThemeBuilder extends DefaultHandler {
public XmlThemeBuilder(ThemeFile theme, ThemeCallback themeCallback) {
mTheme = theme;
mThemeCallback = themeCallback;
mMapsforgeTheme = theme.isMapsforgeTheme();
mScale = CanvasAdapter.getScale();
mScale2 = CanvasAdapter.getScale() * 0.5f;
}
@Override
public void endDocument() {
if (mMapsforgeTheme) {
// Building rule for Mapsforge themes
mRulesList.add(buildingRule());
}
Rule[] rules = new Rule[mRulesList.size()];
for (int i = 0, n = rules.length; i < n; i++)
rules[i] = mRulesList.get(i).onComplete(null);
rules[i] = mRulesList.get(i).onComplete(mMapsforgeTheme ? new int[1] : null);
mRenderTheme = createTheme(rules);
@ -189,14 +202,14 @@ public class XmlThemeBuilder extends DefaultHandler {
}
RenderTheme createTheme(Rule[] rules) {
return new RenderTheme(mMapBackground, mTextScale, rules, mLevels);
return new RenderTheme(mMapBackground, mTextScale, rules, mLevels, mMapsforgeTheme);
}
@Override
public void endElement(String uri, String localName, String qName) {
mElementStack.pop();
if (ELEMENT_NAME_MATCH.equals(localName)) {
if (ELEMENT_NAME_MATCH_MAPSFORGE.equals(localName) || ELEMENT_NAME_MATCH_VTM.equals(localName)) {
mRuleStack.pop();
if (mRuleStack.empty()) {
if (isVisible(mCurrentRule)) {
@ -234,7 +247,7 @@ public class XmlThemeBuilder extends DefaultHandler {
checkState(localName, Element.RENDER_THEME);
createRenderTheme(localName, attributes);
} else if (ELEMENT_NAME_MATCH.equals(localName)) {
} else if (ELEMENT_NAME_MATCH_MAPSFORGE.equals(localName) || ELEMENT_NAME_MATCH_VTM.equals(localName)) {
checkState(localName, Element.RULE);
RuleBuilder rule = createRule(localName, attributes);
if (!mRuleStack.empty() && isVisible(rule)) {
@ -388,9 +401,17 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("NODE".equals(val))
element = Rule.Element.NODE;
} else if ("k".equals(name)) {
keys = value;
if (mMapsforgeTheme) {
if (!"*".equals(value))
keys = value;
} else
keys = value;
} else if ("v".equals(name)) {
values = value;
if (mMapsforgeTheme) {
if (!"*".equals(value))
values = value;
} else
values = value;
} else if ("cat".equals(name)) {
cat = value;
} else if ("closed".equals(name)) {
@ -893,7 +914,8 @@ public class XmlThemeBuilder extends DefaultHandler {
validateExists("version", version, elementName);
if (version > RENDER_THEME_VERSION)
int renderThemeVersion = mMapsforgeTheme ? RENDER_THEME_VERSION_MAPSFORGE : RENDER_THEME_VERSION_VTM;
if (version > renderThemeVersion)
throw new ThemeException("invalid render theme version:" + version);
validateNonNegative("base-stroke-width", baseStrokeWidth);
@ -944,6 +966,11 @@ public class XmlThemeBuilder extends DefaultHandler {
b.themeCallback(mThemeCallback);
String symbol = null;
if (mMapsforgeTheme) {
// Reset default priority
b.priority = DEFAULT_PRIORITY;
}
for (int i = 0; i < attributes.getLength(); i++) {
String name = attributes.getLocalName(i);
String value = attributes.getValue(i);
@ -978,10 +1005,16 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("caption".equals(name))
b.caption = Boolean.parseBoolean(value);
else if ("priority".equals(name))
else if ("priority".equals(name)) {
b.priority = Integer.parseInt(value);
else if ("area-size".equals(name))
if (mMapsforgeTheme) {
// Mapsforge: higher priorities are drawn first (0 = default priority)
// VTM: lower priorities are drawn first (0 = highest priority)
b.priority = FastMath.clamp(DEFAULT_PRIORITY - b.priority, 0, Integer.MAX_VALUE);
}
} else if ("area-size".equals(name))
b.areaSize = Float.parseFloat(value);
else if ("dy".equals(name))
@ -1006,7 +1039,15 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("symbol-scaling".equals(name))
; // no-op
else
else if ("position".equals(name)) {
// Until implement position..
if (b.dy == 0) {
value = "above".equals(value) ? "20" : "-20";
// NB: minus..
b.dy = -Float.parseFloat(value) * mScale;
}
} else
logUnknownAttribute(elementName, name, value, i);
}
@ -1209,4 +1250,19 @@ public class XmlThemeBuilder extends DefaultHandler {
throw new ThemeException("missing attribute " + name
+ " for element: " + elementName);
}
/**
* Building rule for Mapsforge themes.
*/
private RuleBuilder buildingRule() {
ExtrusionBuilder<?> b = mExtrusionBuilder.reset();
b.level(mLevels++);
b.themeCallback(mThemeCallback);
b.colorLine(0xffd9d8d6);
b.colorSide(0xeaecebe9);
b.colorTop(0xeaf9f8f6);
RuleBuilder rule = new RuleBuilder(RuleBuilder.RuleType.POSITIVE, new String[]{Tag.KEY_BUILDING, Tag.KEY_BUILDING_PART}, new String[]{});
rule.element(Rule.Element.WAY).zoom((byte) 17, Byte.MAX_VALUE).style(b);
return rule;
}
}