Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2012 osmdroid authors: Viesturs Zarins, Martin Pearman
* Copyright 2012 Hannes Janetzek
* Copyright 2016-2018 devemux86
* Copyright 2016 Bezzu
* Copyright 2016 Pedinel
* Copyright 2017 Andrey Novikov
* Copyright 2018 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
* 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 .
*/
package org.oscim.layers;
import org.oscim.backend.CanvasAdapter;
import org.oscim.backend.canvas.Paint.Cap;
import org.oscim.core.GeoPoint;
import org.oscim.core.GeometryBuffer;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Point;
import org.oscim.core.Tile;
import org.oscim.event.Gesture;
import org.oscim.event.GestureListener;
import org.oscim.event.MotionEvent;
import org.oscim.map.Map;
import org.oscim.renderer.BucketRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.bucket.LineBucket;
import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.theme.styles.LineStyle;
import org.oscim.utils.FastMath;
import org.oscim.utils.GeoPointUtils;
import org.oscim.utils.async.SimpleWorker;
import org.oscim.utils.geom.LineClipper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* This class draws a path line in given color or texture.
*/
public class PathLayer extends Layer implements GestureListener {
private static final int STROKE_MIN_ZOOM = 12;
/**
* Stores points, converted to the map projection.
*/
protected final ArrayList mPoints;
protected boolean mUpdatePoints;
private final Point mPoint1 = new Point();
private final Point mPoint2 = new Point();
/**
* Line style
*/
LineStyle mLineStyle;
final Worker mWorker;
public PathLayer(Map map, LineStyle style) {
super(map);
mLineStyle = style;
mPoints = new ArrayList<>();
mRenderer = new PathRenderer();
mWorker = new Worker(map);
}
public PathLayer(Map map, int lineColor, float lineWidth) {
this(map, new LineStyle(lineColor, lineWidth, Cap.BUTT));
}
public PathLayer(Map map, int lineColor) {
this(map, lineColor, 2);
}
public void setStyle(LineStyle style) {
mLineStyle = style;
}
public void clearPath() {
if (mPoints.isEmpty())
return;
synchronized (mPoints) {
mPoints.clear();
}
updatePoints();
}
public void setPoints(Collection extends GeoPoint> pts) {
synchronized (mPoints) {
mPoints.clear();
mPoints.addAll(pts);
}
updatePoints();
}
public void addPoint(GeoPoint pt) {
synchronized (mPoints) {
mPoints.add(pt);
}
updatePoints();
}
public void addPoint(int latitudeE6, int longitudeE6) {
synchronized (mPoints) {
mPoints.add(new GeoPoint(latitudeE6, longitudeE6));
}
updatePoints();
}
public void addPoints(Collection extends GeoPoint> pts) {
synchronized (mPoints) {
mPoints.addAll(pts);
}
updatePoints();
}
private void updatePoints() {
mWorker.submit(10);
mUpdatePoints = true;
}
public List getPoints() {
return mPoints;
}
/**
* FIXME To be removed
*
* @deprecated
*/
public void setGeom(GeometryBuffer geom) {
mGeom = geom;
mWorker.submit(10);
}
GeometryBuffer mGeom;
/**
* Draw a great circle. Calculate a point for every 100km along the path.
*
* @param startPoint start point of the great circle
* @param endPoint end point of the great circle
*/
public void addGreatCircle(GeoPoint startPoint, GeoPoint endPoint) {
synchronized (mPoints) {
/* get the great circle path length in meters */
double length = startPoint.sphericalDistance(endPoint);
/* add one point for every 100kms of the great circle path */
int numberOfPoints = (int) (length / 100000);
if (numberOfPoints == 0)
return;
addGreatCircle(startPoint, endPoint, numberOfPoints);
}
}
/**
* Draw a great circle.
*
* @param startPoint start point of the great circle
* @param endPoint end point of the great circle
* @param numberOfPoints number of points to calculate along the path
*/
public void addGreatCircle(GeoPoint startPoint, GeoPoint endPoint,
final int numberOfPoints) {
// adapted from page
// http://compastic.blogspot.co.uk/2011/07/how-to-draw-great-circle-on-map-in.html
// which was adapted from page http://maps.forum.nu/gm_flight_path.html
// convert to radians
double lat1 = startPoint.getLatitude() * Math.PI / 180;
double lon1 = startPoint.getLongitude() * Math.PI / 180;
double lat2 = endPoint.getLatitude() * Math.PI / 180;
double lon2 = endPoint.getLongitude() * Math.PI / 180;
double d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2)
+ Math.cos(lat1) * Math.cos(lat2)
* Math.pow(Math.sin((lon1 - lon2) / 2), 2)));
double bearing = Math.atan2(
Math.sin(lon1 - lon2) * Math.cos(lat2),
Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
* Math.cos(lat2)
* Math.cos(lon1 - lon2))
/ -(Math.PI / 180);
bearing = bearing < 0 ? 360 + bearing : bearing;
for (int i = 0, j = numberOfPoints + 1; i < j; i++) {
double f = 1.0 / numberOfPoints * i;
double A = Math.sin((1 - f) * d) / Math.sin(d);
double B = Math.sin(f * d) / Math.sin(d);
double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2)
* Math.cos(lon2);
double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2)
* Math.sin(lon2);
double z = A * Math.sin(lat1) + B * Math.sin(lat2);
double latN = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
double lonN = Math.atan2(y, x);
addPoint((int) (latN / (Math.PI / 180) * 1E6), (int) (lonN / (Math.PI / 180) * 1E6));
}
}
/***
* everything below runs on GL- and Worker-Thread
***/
final class PathRenderer extends BucketRenderer {
private int mCurX = -1;
private int mCurY = -1;
private int mCurZ = -1;
@Override
public synchronized void update(GLViewport v) {
int tz = 1 << v.pos.zoomLevel;
int tx = (int) (v.pos.x * tz);
int ty = (int) (v.pos.y * tz);
/* update layers when map moved by at least one tile */
if ((tx != mCurX || ty != mCurY || tz != mCurZ)) {
mWorker.submit(100);
mCurX = tx;
mCurY = ty;
mCurZ = tz;
}
Task t = mWorker.poll();
if (t == null)
return;
/* keep position to render relative to current state */
mMapPosition.copy(t.position);
/* compile new layers */
buckets.set(t.buckets.get());
compile();
}
}
static final class Task {
final RenderBuckets buckets = new RenderBuckets();
final MapPosition position = new MapPosition();
}
final class Worker extends SimpleWorker {
// limit coords to maximum resolution of GL.Short
private final int MAX_CLIP = (int) (Short.MAX_VALUE / MapRenderer.COORD_SCALE);
public Worker(Map map) {
super(map, 0, new Task(), new Task());
mClipper = new LineClipper(-MAX_CLIP, -MAX_CLIP, MAX_CLIP, MAX_CLIP);
mPPoints = new float[0];
}
private static final int MIN_DIST = 3;
// pre-projected points
private double[] mPreprojected = new double[2];
// projected points
private float[] mPPoints;
private final LineClipper mClipper;
private int mNumPoints;
@Override
public boolean doWork(Task task) {
int size = mNumPoints;
if (mUpdatePoints) {
synchronized (mPoints) {
mUpdatePoints = false;
mNumPoints = size = mPoints.size();
ArrayList geopoints = mPoints;
double[] points = mPreprojected;
if (size * 2 >= points.length) {
points = mPreprojected = new double[size * 2];
mPPoints = new float[size * 2];
}
for (int i = 0; i < size; i++)
MercatorProjection.project(geopoints.get(i), points, i);
}
} else if (mGeom != null) {
GeometryBuffer geom = mGeom;
mGeom = null;
size = geom.index[0];
double[] points = mPreprojected;
if (size > points.length) {
points = mPreprojected = new double[size * 2];
mPPoints = new float[size * 2];
}
for (int i = 0; i < size; i += 2)
MercatorProjection.project(geom.points[i + 1],
geom.points[i], points,
i >> 1);
mNumPoints = size = size >> 1;
}
if (size == 0) {
if (task.buckets.get() != null) {
task.buckets.clear();
mMap.render();
}
return true;
}
LineBucket ll;
if (mLineStyle.stipple == 0 && mLineStyle.texture == null)
ll = task.buckets.getLineBucket(0);
else
ll = task.buckets.getLineTexBucket(0);
ll.line = mLineStyle;
if (!mLineStyle.fixed && mLineStyle.strokeIncrease > 1)
ll.scale = (float) Math.pow(mLineStyle.strokeIncrease, Math.max(task.position.getZoom() - STROKE_MIN_ZOOM, 0));
mMap.getMapPosition(task.position);
int zoomlevel = task.position.zoomLevel;
task.position.scale = 1 << zoomlevel;
double mx = task.position.x;
double my = task.position.y;
double scale = Tile.SIZE * task.position.scale;
// flip around dateline
int flip = 0;
int maxx = Tile.SIZE << (zoomlevel - 1);
int x = (int) ((mPreprojected[0] - mx) * scale);
int y = (int) ((mPreprojected[1] - my) * scale);
if (x > maxx) {
x -= (maxx * 2);
flip = -1;
} else if (x < -maxx) {
x += (maxx * 2);
flip = 1;
}
mClipper.clipStart(x, y);
float[] projected = mPPoints;
int i = addPoint(projected, 0, x, y);
float prevX = x;
float prevY = y;
float[] segment = null;
for (int j = 2; j < size * 2; j += 2) {
x = (int) ((mPreprojected[j + 0] - mx) * scale);
y = (int) ((mPreprojected[j + 1] - my) * scale);
int flipDirection = 0;
if (x > maxx) {
x -= maxx * 2;
flipDirection = -1;
} else if (x < -maxx) {
x += maxx * 2;
flipDirection = 1;
}
if (flip != flipDirection) {
flip = flipDirection;
if (i > 2)
ll.addLine(projected, i, false);
mClipper.clipStart(x, y);
i = addPoint(projected, 0, x, y);
continue;
}
int clip = mClipper.clipNext(x, y);
if (clip != LineClipper.INSIDE) {
if (i > 2)
ll.addLine(projected, i, false);
if (clip == LineClipper.INTERSECTION) {
/* add line segment */
segment = mClipper.getLine(segment, 0);
ll.addLine(segment, 4, false);
// the prev point is the real point not the clipped point
//prevX = mClipper.outX2;
//prevY = mClipper.outY2;
prevX = x;
prevY = y;
}
i = 0;
// if the end point is inside, add it
if (mClipper.getPrevOutcode() == LineClipper.INSIDE) {
projected[i++] = prevX;
projected[i++] = prevY;
}
continue;
}
float dx = x - prevX;
float dy = y - prevY;
if ((i == 0) || FastMath.absMaxCmp(dx, dy, MIN_DIST)) {
projected[i++] = prevX = x;
projected[i++] = prevY = y;
}
}
if (i > 2)
ll.addLine(projected, i, false);
// trigger redraw to let renderer fetch the result.
mMap.render();
return true;
}
@Override
public void cleanup(Task task) {
task.buckets.clear();
}
private int addPoint(float[] points, int i, int x, int y) {
points[i++] = x;
points[i++] = y;
return i;
}
}
public synchronized boolean contains(float x, float y) {
// Touch min 20 px at baseline mdpi (160dpi)
double distance = Math.max(20 / 2 * CanvasAdapter.getScale(), mLineStyle.width);
for (int i = 0; i < mPoints.size() - 1; i++) {
if (i == 0)
mMap.viewport().toScreenPoint(mPoints.get(i), false, mPoint1);
else {
mPoint1.x = mPoint2.x;
mPoint1.y = mPoint2.y;
}
mMap.viewport().toScreenPoint(mPoints.get(i + 1), false, mPoint2);
if (GeoPointUtils.distanceSegmentPoint(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y, x, y) <= distance)
return true;
}
return false;
}
@Override
public boolean onGesture(Gesture g, MotionEvent e) {
return false;
}
}