318 lines
10 KiB
Java
318 lines
10 KiB
Java
package dk.network42.osmfocus;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.Map;
|
|
import java.util.SortedMap;
|
|
import java.util.TreeMap;
|
|
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Path;
|
|
import android.graphics.RectF;
|
|
import android.util.Log;
|
|
|
|
abstract public class OsmElement implements Comparable<OsmElement> {
|
|
private static final String TAG = "OsmElement";
|
|
protected long mOsmId;
|
|
protected long mOsmVersion;
|
|
protected SortedMap<String, String> tags;
|
|
protected double mCompare=0; // Cached compare value, only calculate once
|
|
boolean mFiltered = false;
|
|
boolean mIsArea = false;
|
|
OsmDB mDb = null;
|
|
|
|
enum Compare implements Comparator<OsmElement> {
|
|
X_COORD {
|
|
public int compare(OsmElement e1, OsmElement e2) {
|
|
if (e1.getX() >= e2.getX()) return 1;
|
|
else return -1;
|
|
}
|
|
},
|
|
X_COORD_DESCENDING {
|
|
public int compare(OsmElement e1, OsmElement e2) {
|
|
if (e2.getX() >= e1.getX()) return 1;
|
|
else return -1;
|
|
}
|
|
},
|
|
Y_COORD {
|
|
public int compare(OsmElement e1, OsmElement e2) {
|
|
if (e1.getY() >= e2.getY()) return 1;
|
|
else return -1;
|
|
}
|
|
},
|
|
Y_COORD_DESCENDING {
|
|
public int compare(OsmElement e1, OsmElement e2) {
|
|
if (e2.getY() >= e1.getY()) return 1;
|
|
else return -1;
|
|
}
|
|
},
|
|
}
|
|
|
|
OsmElement(final long id, final long version) {
|
|
this.mOsmId = id;
|
|
this.mOsmVersion = version;
|
|
this.tags = new TreeMap<String, String>();
|
|
}
|
|
|
|
void addOrUpdateTag(final String tag, final String value) {
|
|
tags.put(tag, value);
|
|
}
|
|
public int getTagCnt() {
|
|
return tags.size();
|
|
}
|
|
public boolean hasTag(final String key, final String value) {
|
|
String keyValue = tags.get(key);
|
|
return keyValue != null && keyValue.equals(value);
|
|
}
|
|
|
|
public String getTagWithKey(final String key) {
|
|
return tags.get(key);
|
|
}
|
|
|
|
public boolean hasTagKey(final String key) {
|
|
return getTagWithKey(key) != null;
|
|
}
|
|
|
|
public boolean tagsIdentical(OsmElement other) {
|
|
if (getTagCnt() != other.getTagCnt())
|
|
return false;
|
|
for(Map.Entry<String,String> entry : tags.entrySet()) {
|
|
String key = entry.getKey();
|
|
String value = entry.getValue();
|
|
String othertag = other.getTagWithKey(key);
|
|
if (othertag == null || value.equalsIgnoreCase(othertag))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public long getOsmId() {
|
|
return mOsmId;
|
|
}
|
|
|
|
public String toString() {
|
|
return "OsmId="+mOsmId;
|
|
}
|
|
|
|
void updateVisuals(OsmDB db) {
|
|
/* See also notes on http://wiki.openstreetmap.org/wiki/The_Future_of_Areas */
|
|
mIsArea = hasTag("area", "yes") || hasTagKey("landuse") || hasTagKey("building") || hasTagKey("natural");
|
|
}
|
|
|
|
public boolean tagIsFiltered(String key, String value) {
|
|
//return false;
|
|
return key.startsWith("kms:") || key.startsWith("osak:") || key.equals("created_by")
|
|
|| key.equals("addr:country") || key.equals("addr:postcode") || key.equals("source");
|
|
}
|
|
|
|
// Number of lines a given text needs given a maximum width
|
|
protected int getTagTextLines(String text, Paint paint, float maxw, float indentw) {
|
|
float w = paint.measureText(text);
|
|
if (w<= maxw)
|
|
return 1;
|
|
return 1 + (int)((w-maxw)/(maxw-indentw)); // FIXME: Approximation, needs char algo
|
|
}
|
|
|
|
protected float formatWrappedText(ArrayList<String> lines, String text, Paint paint, float maxw) {
|
|
float w = paint.measureText(text);
|
|
if (w <= maxw) {
|
|
lines.add(text);
|
|
return w;
|
|
}
|
|
while (!text.isEmpty()) {
|
|
int ch = paint.breakText(text, true, maxw, null);
|
|
lines.add(text.substring(0, ch));
|
|
text = text.substring(ch);
|
|
if (!text.isEmpty())
|
|
text = "\u21AA "+text;
|
|
}
|
|
return maxw;
|
|
}
|
|
|
|
// Special headlines where two tags are combined, currently only highwayType : NameValue
|
|
protected int headLines() {
|
|
int lines = 0;
|
|
if (hasTagKey("highway") && hasTagKey("name"))
|
|
lines += 1;
|
|
//if (hasTagKey("amenity") && hasTagKey("name"))
|
|
// lines += 1;
|
|
return lines;
|
|
}
|
|
|
|
protected float formatWrappedText(ArrayList<String> lines, Paint paint, float maxw) {
|
|
|
|
String head = null;
|
|
float w = 0.0f;
|
|
|
|
// Compact headlines like "ValueOfHighway : ValueOfName"
|
|
if (hasTagKey("highway") && hasTagKey("name")) {
|
|
head = getTagWithKey("highway") + " : '" + getTagWithKey("name") + "'";
|
|
w = formatWrappedText(lines, head, paint, maxw);
|
|
}
|
|
// if (hasTagKey("amenity") && hasTagKey("name")) {
|
|
// head = getTagWithKey("amenity") + " : '" + getTagWithKey("name") + "'";
|
|
// float tmp = formatWrappedText(lines, head, paint, maxw);
|
|
// if (w<tmp)
|
|
// w = tmp;
|
|
// }
|
|
|
|
for(Map.Entry<String,String> entry : tags.entrySet()) {
|
|
String key = entry.getKey();
|
|
String value = entry.getValue();
|
|
if (head != null && (key.equalsIgnoreCase("highway") || key.equalsIgnoreCase("name")))
|
|
continue;
|
|
if (!tagIsFiltered(key, value)) {
|
|
String txt = key+" = "+value;
|
|
float tmp = formatWrappedText(lines, txt, paint, maxw);
|
|
if (w<tmp)
|
|
w = tmp;
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
protected void clipLine(ArrayList<String> lines, int idx, Paint paint, float maxw) {
|
|
//final String sym = "\u21a9"; // bend arrow
|
|
final String sym = " \u21df";
|
|
final String spc = " ";
|
|
String txt = lines.get(idx);
|
|
float txtw = paint.measureText(txt);
|
|
float symw = paint.measureText(sym);
|
|
float spcw = paint.measureText(spc);
|
|
if (txtw+symw < maxw) {
|
|
int spcs = (int)((maxw-(txtw+symw))/spcw);
|
|
for (int ii=0; ii<spcs; ii++)
|
|
txt += spc;
|
|
txt += sym;
|
|
} else {
|
|
int ch = paint.breakText(txt, true, maxw-symw, null);
|
|
txt = txt.substring(0, ch)+sym;
|
|
}
|
|
lines.set(idx, txt);
|
|
}
|
|
|
|
public RectF drawTags(Canvas canvas, RectF viewrect, boolean poi_lines,
|
|
Paint.Align xalign, Paint.Align yalign,
|
|
PaintConfig pcfg, int style) {
|
|
|
|
ArrayList<String> list = new ArrayList<String>();
|
|
final int inset = 3; // inset relative to viewbox
|
|
final int xborder = 4; int yborder = 4; // margin between text and surrounding box
|
|
float x,y, w;
|
|
int lines;
|
|
float round = 1.0f;
|
|
float lspace = pcfg.tag.getTextSize()+1;
|
|
|
|
float maxx = viewrect.width() * 0.49f;
|
|
float maxy = viewrect.height() * 0.49f;
|
|
float maxw = maxx-2*xborder-inset;
|
|
int maxl = (int)(maxy/lspace);
|
|
|
|
w = formatWrappedText(list, pcfg.tag, maxw);
|
|
lines = list.size();
|
|
|
|
if (lines>maxl) {
|
|
lines=maxl;
|
|
clipLine(list, maxl-1, pcfg.tag, maxw);
|
|
}
|
|
|
|
if (xalign == Paint.Align.LEFT) { // x,y is upper left corner of box
|
|
x = viewrect.left+inset;
|
|
} else if (xalign == Paint.Align.RIGHT) {
|
|
x = viewrect.right-w-2*xborder-inset;
|
|
} else {
|
|
x = (viewrect.right-viewrect.left)/2-w/2-inset;
|
|
}
|
|
if (yalign == Paint.Align.LEFT) {
|
|
y = viewrect.top+inset;
|
|
} else if (yalign == Paint.Align.RIGHT) {
|
|
y = viewrect.bottom-(2*yborder+lines*lspace)-inset;
|
|
} else {
|
|
y = (viewrect.bottom-viewrect.top)/2-(lines*lspace)/2-inset;
|
|
}
|
|
|
|
// Draw box
|
|
RectF rrect = new RectF(x,y,x+w+2*xborder,y+2*yborder+lines*lspace);
|
|
|
|
if (headLines() > 0) {
|
|
drawFancyFrame(canvas, pcfg.tagback, pcfg.tagback3, rrect, lines-1, lspace);
|
|
} else {
|
|
canvas.drawRoundRect(rrect, 2*round, 2*round, pcfg.tagback3);
|
|
}
|
|
canvas.drawRoundRect(rrect, 2*round, 2*round, pcfg.focus2[style]);
|
|
|
|
// Draw text
|
|
for (int ii=0; ii<lines; ii++) {
|
|
canvas.drawText(list.get(ii), x+xborder, y+lspace, pcfg.tag);
|
|
y += lspace;
|
|
|
|
}
|
|
|
|
if (poi_lines) {
|
|
// Draw line to point on map
|
|
pcfg.pts[0] = getX();
|
|
pcfg.pts[1] = getY();
|
|
pcfg.viewmatrix.mapPoints(pcfg.pts);
|
|
//x = rrect.centerX();
|
|
x = xalign == Paint.Align.LEFT ? rrect.right : (xalign == Paint.Align.RIGHT ? rrect.left : viewrect.centerX());
|
|
y = yalign == Paint.Align.LEFT ? rrect.bottom : (yalign == Paint.Align.RIGHT ? rrect.top : viewrect.centerY());
|
|
double [] newpts = GeoMath.shortenLine(pcfg.pts[0], pcfg.pts[1], x, y, 7.5f*pcfg.densityScale);
|
|
//Log.d(TAG, "Tag: pts="+ee.getX()+","+ee.getY()+" screenpts="+pcfg.pts[0]+","+pcfg.pts[1]);
|
|
canvas.drawLine(x, y, (float)newpts[0], (float)newpts[1], pcfg.focus2[style]);
|
|
}
|
|
return rrect;
|
|
}
|
|
|
|
public void drawFancyFrame(Canvas canvas, Paint style1, Paint style2, RectF rrect, int lines, float lspace)
|
|
{
|
|
float x = rrect.left;
|
|
float y = rrect.top;
|
|
float w = rrect.right-rrect.left;
|
|
float round = 1.0f;
|
|
RectF roundrr = new RectF();
|
|
Path pp = new Path();
|
|
Path pp2 = new Path();
|
|
pp.moveTo(x, y+lspace+2); // Top
|
|
pp.lineTo(x+w, y+lspace+2);
|
|
pp.lineTo(x+w, y+round);
|
|
//roundrr.set(rrect.left, rrect.top, rrect.right, rrect.bottom);
|
|
roundrr.set(rrect.right-2*round, rrect.top, rrect.right, rrect.top+2*round);
|
|
pp.arcTo(roundrr, 0, -90);
|
|
pp.lineTo(x+round, y);
|
|
roundrr.set(rrect.left, rrect.top, rrect.left+2*round, rrect.top+2*round);
|
|
pp.arcTo(roundrr, -90, -90);
|
|
canvas.drawPath(pp, style1);
|
|
pp2.moveTo(x, y+lspace+2); // Bottom
|
|
pp2.lineTo(x+w, y+lspace+2); // Start upper-right
|
|
//pp2.lineTo(x+w, y+lines*lspace-round);
|
|
roundrr.set(rrect.right-2*round, rrect.bottom-2*round, rrect.right, rrect.bottom);
|
|
pp2.arcTo(roundrr, 0, 90);
|
|
//pp2.lineTo(x+round, y+lines*lspace);
|
|
roundrr.set(rrect.left, rrect.bottom-2*round, rrect.left+2*round, rrect.bottom);
|
|
pp2.arcTo(roundrr, 90, 90);
|
|
canvas.drawPath(pp2, style2);
|
|
}
|
|
|
|
public void draw(Canvas canvas, OsmDB db, PaintConfig pcfg) {}
|
|
public void highlight(Canvas canvas, OsmDB db, PaintConfig pcfg, int style) {}
|
|
public abstract double distTo(double lon, double lat_merc);
|
|
public abstract double angleTo(double lon, double lat_merc);
|
|
|
|
// For non-point object these coords are 'close points' - see e.g. compareSetDistTo() for OsmWay
|
|
float getX() { return 0; };
|
|
float getY() { return 0; };
|
|
|
|
public int compareTo(OsmElement e) {
|
|
// int subtraction does not work with small deltas
|
|
if (mCompare >= e.mCompare) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
public void compareSetDistTo(double lon, double lat_merc) { mCompare = distTo(lon, lat_merc); }
|
|
public void compareSetAngleTo(double lon, double lat_merc, double offset) { mCompare = angleTo(lon, lat_merc)+offset; }
|
|
public double compareGet() { return mCompare; }
|
|
}
|