Merge remote-tracking branch 'origin/master'
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<property name="use.dir" value="../OsmAnd-java/src" />
|
<property name="use.dir" value="../OsmAnd-java/src" />
|
||||||
<property name="use.absolute.dir" location="${use.dir}" />
|
<property name="use.absolute.dir" location="${use.dir}" />
|
||||||
<property name="raw.absolute.dir" location="raw" />
|
<property name="src.absolute.dir" location="src" />
|
||||||
<property name="base.dir" location="." />
|
<property name="base.dir" location="." />
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,22 +29,22 @@
|
||||||
<include name="**/*"/>
|
<include name="**/*"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="${use.absolute.dir}/net/osmand/router/">
|
<copy todir="${src.absolute.dir}/net/osmand/router/">
|
||||||
<fileset dir="../../resources/routing/" >
|
<fileset dir="../../resources/routing/" >
|
||||||
<include name="*.xml"/>
|
<include name="*.xml"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="${use.absolute.dir}/net/osmand/render/">
|
<copy todir="${src.absolute.dir}/net/osmand/render/">
|
||||||
<fileset dir="../../resources/rendering_styles/" >
|
<fileset dir="../../resources/rendering_styles/" >
|
||||||
<include name="*.xml"/>
|
<include name="*.xml"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="${use.absolute.dir}/net/osmand/osm/">
|
<copy todir="${src.absolute.dir}/net/osmand/osm/">
|
||||||
<fileset dir="../../resources/obf_creation/" >
|
<fileset dir="../../resources/obf_creation/" >
|
||||||
<include name="rendering_types.xml"/>
|
<include name="rendering_types.xml"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="${use.absolute.dir}/net/osmand/map/">
|
<copy todir="${src.absolute.dir}/net/osmand/map/">
|
||||||
<fileset dir="../../resources/countries-info/" >
|
<fileset dir="../../resources/countries-info/" >
|
||||||
<include name="countries.reginfo"/>
|
<include name="countries.reginfo"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
|
@ -56,13 +56,6 @@
|
||||||
|
|
||||||
<!-- Extra target -->
|
<!-- Extra target -->
|
||||||
<target name="-pre-build" depends="copy_resources,fix_apostrophe_issues">
|
<target name="-pre-build" depends="copy_resources,fix_apostrophe_issues">
|
||||||
<delete dir="${raw.absolute.dir}" verbose="${verbose}" />
|
|
||||||
<mkdir dir="${raw.absolute.dir}/classes" />
|
|
||||||
<copy todir="${raw.absolute.dir}">
|
|
||||||
<fileset dir="${use.absolute.dir}">
|
|
||||||
<exclude name="**/*.java" />
|
|
||||||
</fileset>
|
|
||||||
</copy>
|
|
||||||
<if>
|
<if>
|
||||||
<condition>
|
<condition>
|
||||||
<isset property="versionFeatures" />
|
<isset property="versionFeatures" />
|
||||||
|
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1,017 B After Width: | Height: | Size: 1,014 B |
Before Width: | Height: | Size: 857 B After Width: | Height: | Size: 849 B |
Before Width: | Height: | Size: 857 B After Width: | Height: | Size: 849 B |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -75,6 +75,9 @@ public class RendererRegistry {
|
||||||
private RenderingRulesStorage loadRenderer(String name, final Map<String, RenderingRulesStorage> loadedRenderers,
|
private RenderingRulesStorage loadRenderer(String name, final Map<String, RenderingRulesStorage> loadedRenderers,
|
||||||
final Map<String, String> renderingConstants) throws IOException, XmlPullParserException {
|
final Map<String, String> renderingConstants) throws IOException, XmlPullParserException {
|
||||||
InputStream is = getInputStream(name);
|
InputStream is = getInputStream(name);
|
||||||
|
if(is == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
XmlPullParser parser = PlatformUtil.newXMLPullParser();
|
XmlPullParser parser = PlatformUtil.newXMLPullParser();
|
||||||
parser.setInput(is, "UTF-8");
|
parser.setInput(is, "UTF-8");
|
||||||
|
|
|
@ -1,494 +0,0 @@
|
||||||
package net.osmand.plus.render;
|
|
||||||
|
|
||||||
|
|
||||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
|
||||||
import gnu.trove.procedure.TIntObjectProcedure;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.osmand.FloatMath;
|
|
||||||
import net.osmand.QuadRect;
|
|
||||||
import net.osmand.RenderingContext;
|
|
||||||
import net.osmand.binary.BinaryMapDataObject;
|
|
||||||
import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
|
|
||||||
import net.osmand.data.QuadTree;
|
|
||||||
import net.osmand.plus.ClientContext;
|
|
||||||
import net.osmand.plus.api.render.Canvas;
|
|
||||||
import net.osmand.plus.api.render.Color;
|
|
||||||
import net.osmand.plus.api.render.Paint;
|
|
||||||
import net.osmand.plus.api.render.Paint.Align;
|
|
||||||
import net.osmand.plus.api.render.Paint.Style;
|
|
||||||
import net.osmand.plus.api.render.Path;
|
|
||||||
import net.osmand.plus.api.render.Typeface;
|
|
||||||
import net.osmand.render.RenderingRuleSearchRequest;
|
|
||||||
import net.osmand.render.RenderingRulesStorage;
|
|
||||||
import net.sf.junidecode.Junidecode;
|
|
||||||
|
|
||||||
import org.w3c.dom.css.Rect;
|
|
||||||
|
|
||||||
|
|
||||||
public class TextRenderer {
|
|
||||||
|
|
||||||
private Paint paintText;
|
|
||||||
private final ClientContext context;
|
|
||||||
private Paint paintIcon;
|
|
||||||
|
|
||||||
static class TextDrawInfo {
|
|
||||||
|
|
||||||
public TextDrawInfo(String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
String text = null;
|
|
||||||
Path drawOnPath = null;
|
|
||||||
QuadRect bounds = null;
|
|
||||||
float vOffset = 0;
|
|
||||||
float centerX = 0;
|
|
||||||
float pathRotate = 0;
|
|
||||||
float centerY = 0;
|
|
||||||
float textSize = 0;
|
|
||||||
float minDistance = 0;
|
|
||||||
int textColor = Color.BLACK;
|
|
||||||
int textShadow = 0;
|
|
||||||
int textWrap = 0;
|
|
||||||
boolean bold = false;
|
|
||||||
String shieldRes = null;
|
|
||||||
int textOrder = 100;
|
|
||||||
|
|
||||||
public void fillProperties(RenderingRuleSearchRequest render, float centerX, float centerY) {
|
|
||||||
this.centerX = centerX;
|
|
||||||
this.centerY = centerY + render.getIntPropertyValue(render.ALL.R_TEXT_DY, 0);
|
|
||||||
// used only for draw on path where centerY doesn't play role
|
|
||||||
this.vOffset = render.getIntPropertyValue(render.ALL.R_TEXT_DY, 0);
|
|
||||||
textColor = render.getIntPropertyValue(render.ALL.R_TEXT_COLOR);
|
|
||||||
if (textColor == 0) {
|
|
||||||
textColor = Color.BLACK;
|
|
||||||
}
|
|
||||||
textSize = render.getIntPropertyValue(render.ALL.R_TEXT_SIZE);
|
|
||||||
textShadow = render.getIntPropertyValue(render.ALL.R_TEXT_HALO_RADIUS, 0);
|
|
||||||
textWrap = render.getIntPropertyValue(render.ALL.R_TEXT_WRAP_WIDTH, 0);
|
|
||||||
bold = render.getIntPropertyValue(render.ALL.R_TEXT_BOLD, 0) > 0;
|
|
||||||
minDistance = render.getIntPropertyValue(render.ALL.R_TEXT_MIN_DISTANCE, 0);
|
|
||||||
if (render.isSpecified(render.ALL.R_TEXT_SHIELD)) {
|
|
||||||
shieldRes = render.getStringPropertyValue(render.ALL.R_TEXT_SHIELD);
|
|
||||||
}
|
|
||||||
textOrder = render.getIntPropertyValue(render.ALL.R_TEXT_ORDER, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextRenderer(ClientContext context) {
|
|
||||||
this.context = context;
|
|
||||||
paintText = context.getRendererAPI().newPaint();
|
|
||||||
paintText.setStyle(Style.FILL);
|
|
||||||
paintText.setStrokeWidth(1);
|
|
||||||
paintText.setColor(Color.BLACK);
|
|
||||||
paintText.setTextAlign(Align.CENTER);
|
|
||||||
paintText.setTypeface(Typeface.create("Droid Serif", Typeface.NORMAL)); //$NON-NLS-1$
|
|
||||||
paintText.setAntiAlias(true);
|
|
||||||
|
|
||||||
paintIcon = context.getRendererAPI().newPaint();
|
|
||||||
paintIcon.setStyle(Style.STROKE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Paint getPaintText() {
|
|
||||||
return paintText;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float sqr(float a) {
|
|
||||||
return a * a;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean intersects(QuadRect tRect, float tRot, QuadRect sRect, float sRot) {
|
|
||||||
if (FloatMath.abs(tRot) < FloatMath.PI / 15 && FloatMath.abs(sRot) < FloatMath.PI / 15) {
|
|
||||||
return QuadRect.intersects(tRect, sRect);
|
|
||||||
}
|
|
||||||
double dist = FloatMath.sqrt(sqr(tRect.centerX() - sRect.centerX()) + sqr(tRect.centerY() - sRect.centerY()));
|
|
||||||
if (dist < 3) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// difference close to 90/270 degrees
|
|
||||||
if (FloatMath.abs(FloatMath.cos(tRot - sRot)) < 0.3) {
|
|
||||||
// rotate one rectangle to 90 degrees
|
|
||||||
tRot += FloatMath.PI / 2;
|
|
||||||
float l = tRect.centerX() - tRect.height() / 2;
|
|
||||||
float t = tRect.centerY() - tRect.width() / 2;
|
|
||||||
tRect = new QuadRect(l, t, l + tRect.height(), t + tRect.width());
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine difference close to 180/0 degrees
|
|
||||||
if (FloatMath.abs(FloatMath.sin(tRot - sRot)) < 0.3) {
|
|
||||||
// rotate t box
|
|
||||||
// (calculate offset for t center suppose we rotate around s center)
|
|
||||||
float diff = (float) (-FloatMath.atan2(tRect.centerX() - sRect.centerX(), tRect.centerY() - sRect.centerY()) + Math.PI / 2);
|
|
||||||
diff -= sRot;
|
|
||||||
float left = sRect.centerX() + dist * FloatMath.cos(diff) - tRect.width() / 2f;
|
|
||||||
float top = sRect.centerY() - dist * FloatMath.sin(diff) - tRect.height() / 2f;
|
|
||||||
QuadRect nRect = new QuadRect(left, top, left + tRect.width(), top + tRect.height());
|
|
||||||
return QuadRect.intersects(nRect, sRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO other cases not covered
|
|
||||||
return QuadRect.intersects(tRect, sRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawTestBox(Canvas cv, RectF r, float rot, String text) {
|
|
||||||
cv.save();
|
|
||||||
cv.translate(r.centerX(), r.centerY());
|
|
||||||
cv.rotate((float) (rot * 180 / Math.PI));
|
|
||||||
RectF rs = new RectF(-r.width() / 2, -r.height() / 2, r.width() / 2, r.height() / 2);
|
|
||||||
cv.drawRect(rs, paintIcon);
|
|
||||||
if (text != null) {
|
|
||||||
paintText.setTextSize(paintText.getTextSize() - 4);
|
|
||||||
cv.drawText(text, rs.centerX(), rs.centerY(), paintText);
|
|
||||||
paintText.setTextSize(paintText.getTextSize() + 4);
|
|
||||||
}
|
|
||||||
cv.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TextDrawInfo> tempSearch = new ArrayList<TextDrawInfo>();
|
|
||||||
|
|
||||||
private boolean findTextIntersection(Canvas cv, RenderingContext rc, QuadTree<TextDrawInfo> boundIntersections, TextDrawInfo text) {
|
|
||||||
// for test purposes
|
|
||||||
// drawTestBox(cv, text.bounds, text.pathRotate, text.text);
|
|
||||||
boundIntersections.queryInBox(text.bounds, tempSearch);
|
|
||||||
for (int i = 0; i < tempSearch.size(); i++) {
|
|
||||||
TextDrawInfo t = tempSearch.get(i);
|
|
||||||
if (intersects(text.bounds, text.pathRotate, t.bounds, t.pathRotate)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (text.minDistance > 0) {
|
|
||||||
QuadRect boundsSearch = new QuadRect(text.bounds);
|
|
||||||
boundsSearch.inset(-rc.getDensityValue(FloatMath.max(5.0f, text.minDistance)), -rc.getDensityValue(15));
|
|
||||||
boundIntersections.queryInBox(boundsSearch, tempSearch);
|
|
||||||
// drawTestBox(cv, &boundsSearch, text.pathRotate, paintIcon, text.text, NULL/*paintText*/);
|
|
||||||
for (int i = 0; i < tempSearch.size(); i++) {
|
|
||||||
TextDrawInfo t = tempSearch.get(i);
|
|
||||||
if (t.minDistance > 0 && t.text.equals(text.text) &&
|
|
||||||
intersects(boundsSearch, text.pathRotate, t.bounds, t.pathRotate)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boundIntersections.insert(text, text.bounds);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawTextOnCanvas(Canvas cv, String text, float centerX, float centerY, Paint paint, float textShadow) {
|
|
||||||
if (textShadow > 0) {
|
|
||||||
int c = paintText.getColor();
|
|
||||||
paintText.setStyle(Style.STROKE);
|
|
||||||
paintText.setColor(Color.WHITE);
|
|
||||||
paintText.setStrokeWidth(2 + textShadow);
|
|
||||||
cv.drawText(text, centerX, centerY, paint);
|
|
||||||
// reset
|
|
||||||
paintText.setStrokeWidth(2);
|
|
||||||
paintText.setStyle(Style.FILL);
|
|
||||||
paintText.setColor(c);
|
|
||||||
}
|
|
||||||
cv.drawText(text, centerX, centerY, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawTextOverCanvas(RenderingContext rc, Canvas cv, boolean useEnglishNames) {
|
|
||||||
int size = rc.textToDraw.size();
|
|
||||||
|
|
||||||
// 1. Sort text using text order
|
|
||||||
Collections.sort(rc.textToDraw, new Comparator<TextDrawInfo>() {
|
|
||||||
@Override
|
|
||||||
public int compare(TextDrawInfo object1, TextDrawInfo object2) {
|
|
||||||
return object1.textOrder - object2.textOrder;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
QuadRect r = new QuadRect(0, 0, rc.width, rc.height);
|
|
||||||
r.inset(-100, -100);
|
|
||||||
QuadTree<TextDrawInfo> nonIntersectedBounds = new QuadTree<TextDrawInfo>(r, 4, 0.6f);
|
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
TextDrawInfo text = rc.textToDraw.get(i);
|
|
||||||
if (text.text != null && text.text.length() > 0) {
|
|
||||||
if (useEnglishNames) {
|
|
||||||
text.text = Junidecode.unidecode(text.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sest text size before finding intersection (it is used there)
|
|
||||||
float textSize = rc.getDensityValue(text.textSize);
|
|
||||||
paintText.setTextSize(textSize);
|
|
||||||
paintText.setFakeBoldText(text.bold);
|
|
||||||
paintText.setColor(text.textColor);
|
|
||||||
// align center y
|
|
||||||
text.centerY += (-paintText.ascent());
|
|
||||||
|
|
||||||
// calculate if there is intersection
|
|
||||||
boolean intersects = findTextIntersection(cv, rc, nonIntersectedBounds, text);
|
|
||||||
if (!intersects) {
|
|
||||||
if (text.drawOnPath != null) {
|
|
||||||
if (text.textShadow > 0) {
|
|
||||||
paintText.setColor(Color.WHITE);
|
|
||||||
paintText.setStyle(Style.STROKE);
|
|
||||||
paintText.setStrokeWidth(2 + text.textShadow);
|
|
||||||
cv.drawTextOnPath(text.text, text.drawOnPath, 0, text.vOffset, paintText);
|
|
||||||
// reset
|
|
||||||
paintText.setStyle(Style.FILL);
|
|
||||||
paintText.setStrokeWidth(2);
|
|
||||||
paintText.setColor(text.textColor);
|
|
||||||
}
|
|
||||||
cv.drawTextOnPath(text.text, text.drawOnPath, 0, text.vOffset, paintText);
|
|
||||||
} else {
|
|
||||||
if (text.shieldRes != null) {
|
|
||||||
Bitmap ico = RenderingIcons.getIcon(context, text.shieldRes);
|
|
||||||
if (ico != null) {
|
|
||||||
if (rc.getDensityValue(1) != 1) {
|
|
||||||
float left = text.centerX - rc.getDensityValue(ico.getWidth() / 2) - 0.5f;
|
|
||||||
float top = text.centerY - rc.getDensityValue(ico.getHeight() / 2) - rc.getDensityValue(4.5f);
|
|
||||||
Rect rec = new Rect(0, 0, ico.getWidth(), ico.getHeight());
|
|
||||||
cv.drawBitmap(ico, rec,
|
|
||||||
new RectF(left, top, left + rc.getDensityValue(ico.getWidth()), top
|
|
||||||
+ rc.getDensityValue(ico.getHeight())), paintIcon);
|
|
||||||
} else {
|
|
||||||
float left = text.centerX - ico.getWidth() / 2 - 0.5f;
|
|
||||||
float top = text.centerY - ico.getHeight() / 2 - rc.getDensityValue(4.5f);
|
|
||||||
cv.drawBitmap(ico, left, top, paintIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drawWrappedText(cv, text, textSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawWrappedText(Canvas cv, TextDrawInfo text, float textSize) {
|
|
||||||
if (text.textWrap == 0) {
|
|
||||||
// set maximum for all text
|
|
||||||
text.textWrap = 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text.text.length() > text.textWrap) {
|
|
||||||
int start = 0;
|
|
||||||
int end = text.text.length();
|
|
||||||
int lastSpace = -1;
|
|
||||||
int line = 0;
|
|
||||||
int pos = 0;
|
|
||||||
int limit = 0;
|
|
||||||
while (pos < end) {
|
|
||||||
lastSpace = -1;
|
|
||||||
limit += text.textWrap;
|
|
||||||
while (pos < limit && pos < end) {
|
|
||||||
if (!Character.isLetterOrDigit(text.text.charAt(pos))) {
|
|
||||||
lastSpace = pos;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
if (lastSpace == -1 || pos == end) {
|
|
||||||
drawTextOnCanvas(cv, text.text.substring(start, pos), text.centerX, text.centerY + line * (textSize + 2), paintText,
|
|
||||||
text.textShadow);
|
|
||||||
start = pos;
|
|
||||||
} else {
|
|
||||||
drawTextOnCanvas(cv, text.text.substring(start, lastSpace), text.centerX, text.centerY + line * (textSize + 2),
|
|
||||||
paintText, text.textShadow);
|
|
||||||
start = lastSpace + 1;
|
|
||||||
limit += (start - pos) - 1;
|
|
||||||
}
|
|
||||||
line++;
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
drawTextOnCanvas(cv, text.text, text.centerX, text.centerY, paintText, text.textShadow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTextDrawInfo(BinaryMapDataObject o, RenderingRuleSearchRequest render, RenderingContext rc, TagValuePair pair, float xMid, float yMid,
|
|
||||||
Path path, PointF[] points, String name, String tagName) {
|
|
||||||
render.setInitialTagValueZoom(pair.tag, pair.value, rc.zoom, o);
|
|
||||||
render.setIntFilter(render.ALL.R_TEXT_LENGTH, name.length());
|
|
||||||
render.setStringFilter(render.ALL.R_NAME_TAG, tagName);
|
|
||||||
if(render.search(RenderingRulesStorage.TEXT_RULES)){
|
|
||||||
if(render.getIntPropertyValue(render.ALL.R_TEXT_SIZE) > 0){
|
|
||||||
TextDrawInfo text = new TextDrawInfo(name);
|
|
||||||
text.fillProperties(render, xMid, yMid);
|
|
||||||
paintText.setTextSize(rc.getDensityValue(text.textSize));
|
|
||||||
Rect bs = new Rect();
|
|
||||||
paintText.getTextBounds(name, 0, name.length(), bs);
|
|
||||||
text.bounds = new QuadRect(bs.left, bs.top, bs.right, bs.bottom);
|
|
||||||
text.bounds.inset(-rc.getDensityValue(3), -rc.getDensityValue(10));
|
|
||||||
boolean display = true;
|
|
||||||
if(path != null) {
|
|
||||||
text.drawOnPath = path;
|
|
||||||
display = calculatePathToRotate(rc, text, points,
|
|
||||||
render.getIntPropertyValue(render.ALL.R_TEXT_ON_PATH, 0) != 0);
|
|
||||||
}
|
|
||||||
if(text.drawOnPath == null) {
|
|
||||||
text.bounds.offset(text.centerX, text.centerY);
|
|
||||||
// shift to match alignment
|
|
||||||
text.bounds.offset(-text.bounds.width()/2, 0);
|
|
||||||
} else {
|
|
||||||
text.bounds.offset(text.centerX - text.bounds.width()/2, text.centerY - text.bounds.height()/2);
|
|
||||||
}
|
|
||||||
if(display) {
|
|
||||||
rc.textToDraw.add(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderText(final BinaryMapDataObject obj, final RenderingRuleSearchRequest render, final RenderingContext rc,
|
|
||||||
final TagValuePair pair, final float xMid, final float yMid, final Path path, final PointF[] points) {
|
|
||||||
TIntObjectHashMap<String> map = obj.getObjectNames();
|
|
||||||
if (map != null) {
|
|
||||||
map.forEachEntry(new TIntObjectProcedure<String>() {
|
|
||||||
@Override
|
|
||||||
public boolean execute(int tag, String name) {
|
|
||||||
if (name != null && name.trim().length() > 0) {
|
|
||||||
createTextDrawInfo(obj, render, rc, pair, xMid, yMid, path, points, name, tag == obj.getMapIndex().nameEncodingType ? ""
|
|
||||||
: obj.getMapIndex().decodeType(tag).tag);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean calculatePathToRotate(RenderingContext rc, TextDrawInfo p, PointF[] points, boolean drawOnPath) {
|
|
||||||
int len = points.length;
|
|
||||||
if (!drawOnPath) {
|
|
||||||
p.drawOnPath = null;
|
|
||||||
// simply calculate rotation of path used for shields
|
|
||||||
float px = 0;
|
|
||||||
float py = 0;
|
|
||||||
for (int i = 1; i < len; i++) {
|
|
||||||
px += points[i].x - points[i - 1].x;
|
|
||||||
py += points[i].y - points[i - 1].y;
|
|
||||||
}
|
|
||||||
if (px != 0 || py != 0) {
|
|
||||||
p.pathRotate = (float) (-FloatMath.atan2(px, py) + FloatMath.PI / 2);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean inverse = false;
|
|
||||||
float roadLength = 0;
|
|
||||||
boolean prevInside = false;
|
|
||||||
float visibleRoadLength = 0;
|
|
||||||
float textw = p.bounds.width();
|
|
||||||
int last = 0;
|
|
||||||
int startVisible = 0;
|
|
||||||
float[] distances = new float[points.length - 1];
|
|
||||||
|
|
||||||
float normalTextLen = 1.5f * textw;
|
|
||||||
for (int i = 0; i < len; i++, last++) {
|
|
||||||
boolean inside = points[i].x >= 0 && points[i].x <= rc.width &&
|
|
||||||
points[i].x >= 0 && points[i].y <= rc.height;
|
|
||||||
if (i > 0) {
|
|
||||||
float d = FloatMath.sqrt(sqr(points[i].x - points[i - 1].x) +
|
|
||||||
sqr(points[i].y - points[i - 1].y));
|
|
||||||
distances[i-1]= d;
|
|
||||||
roadLength += d;
|
|
||||||
if(inside) {
|
|
||||||
visibleRoadLength += d;
|
|
||||||
if(!prevInside) {
|
|
||||||
startVisible = i - 1;
|
|
||||||
}
|
|
||||||
} else if(prevInside) {
|
|
||||||
if(visibleRoadLength >= normalTextLen) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
visibleRoadLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
prevInside = inside;
|
|
||||||
}
|
|
||||||
if (textw >= roadLength) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int startInd = 0;
|
|
||||||
int endInd = len;
|
|
||||||
|
|
||||||
if(textw < visibleRoadLength && last - startVisible > 1) {
|
|
||||||
startInd = startVisible;
|
|
||||||
endInd = last;
|
|
||||||
// display long road name in center
|
|
||||||
if (visibleRoadLength > 3 * textw) {
|
|
||||||
boolean ch ;
|
|
||||||
do {
|
|
||||||
ch = false;
|
|
||||||
if(endInd - startInd > 2 && visibleRoadLength - distances[startInd] > normalTextLen){
|
|
||||||
visibleRoadLength -= distances[startInd];
|
|
||||||
startInd++;
|
|
||||||
ch = true;
|
|
||||||
}
|
|
||||||
if(endInd - startInd > 2 && visibleRoadLength - distances[endInd - 2] > normalTextLen){
|
|
||||||
visibleRoadLength -= distances[endInd - 2];
|
|
||||||
endInd--;
|
|
||||||
ch = true;
|
|
||||||
}
|
|
||||||
} while(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// shrink path to display more text
|
|
||||||
if (startInd > 0 || endInd < len) {
|
|
||||||
// find subpath
|
|
||||||
Path path = new Path();
|
|
||||||
for (int i = startInd; i < endInd; i++) {
|
|
||||||
if (i == startInd) {
|
|
||||||
path.moveTo(points[i].x, points[i].y);
|
|
||||||
} else {
|
|
||||||
path.lineTo(points[i].x, points[i].y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.drawOnPath = path;
|
|
||||||
}
|
|
||||||
// calculate vector of the road (px, py) to proper rotate it
|
|
||||||
float px = 0;
|
|
||||||
float py = 0;
|
|
||||||
for (int i = startInd + 1; i < endInd; i++) {
|
|
||||||
px += points[i].x - points[i - 1].x;
|
|
||||||
py += points[i].y - points[i - 1].y;
|
|
||||||
}
|
|
||||||
float scale = 0.5f;
|
|
||||||
float plen = (float) FloatMath.sqrt(px * px + py * py);
|
|
||||||
// vector ox,oy orthogonal to px,py to measure height
|
|
||||||
float ox = -py;
|
|
||||||
float oy = px;
|
|
||||||
if(plen > 0) {
|
|
||||||
float rot = (float) (-FloatMath.atan2(px, py) + FloatMath.PI / 2);
|
|
||||||
if (rot < 0) rot += FloatMath.PI * 2;
|
|
||||||
if (rot > FloatMath.PI / 2f && rot < 3 * FloatMath.PI / 2f) {
|
|
||||||
rot += FloatMath.PI;
|
|
||||||
inverse = true;
|
|
||||||
ox = -ox;
|
|
||||||
oy = -oy;
|
|
||||||
}
|
|
||||||
p.pathRotate = rot;
|
|
||||||
ox *= (p.bounds.height() / plen) / 2;
|
|
||||||
oy *= (p.bounds.height() / plen) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.centerX = points[startInd].x + scale * px + ox;
|
|
||||||
p.centerY = points[startInd].y + scale * py + oy;
|
|
||||||
p.vOffset += p.textSize / 2 - 1;
|
|
||||||
// p.hOffset = 0;
|
|
||||||
|
|
||||||
if (inverse) {
|
|
||||||
Path path = new Path();
|
|
||||||
for (int i = endInd - 1; i >= startInd; i--) {
|
|
||||||
if (i == endInd - 1) {
|
|
||||||
path.moveTo(points[i].x, points[i].y);
|
|
||||||
} else {
|
|
||||||
path.lineTo(points[i].x, points[i].y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.drawOnPath = path;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|