vtm-app: revive / update with latest VTM, closes #90
This commit is contained in:
392
vtm-app/src/org/oscim/app/location/Compass.java
Normal file
392
vtm-app/src/org/oscim/app/location/Compass.java
Normal file
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright 2013 Ahmad Saleem
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
*
|
||||
* 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.app.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.map.Map;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Compass extends Layer implements SensorEventListener,
|
||||
Map.UpdateListener {
|
||||
|
||||
// final static Logger log = LoggerFactory.getLogger(Compass.class);
|
||||
|
||||
public enum Mode {
|
||||
OFF, C2D, C3D,
|
||||
}
|
||||
|
||||
private final SensorManager mSensorManager;
|
||||
private final ImageView mArrowView;
|
||||
|
||||
// private final float[] mRotationM = new float[9];
|
||||
private final float[] mRotationV = new float[3];
|
||||
|
||||
// private float[] mAccelV = new float[3];
|
||||
// private float[] mMagnetV = new float[3];
|
||||
// private boolean mLastAccelerometerSet;
|
||||
// private boolean mLastMagnetometerSet;
|
||||
|
||||
private float mCurRotation;
|
||||
private float mCurTilt;
|
||||
|
||||
private boolean mControlOrientation;
|
||||
|
||||
private Mode mMode = Mode.OFF;
|
||||
private int mListeners;
|
||||
|
||||
@Override
|
||||
public void onMapEvent(Event e, MapPosition mapPosition) {
|
||||
if (!mControlOrientation) {
|
||||
float rotation = -mapPosition.bearing;
|
||||
adjustArrow(rotation, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public Compass(Context context, Map map) {
|
||||
super(map);
|
||||
|
||||
mSensorManager = (SensorManager) context
|
||||
.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
// List<Sensor> s = mSensorManager.getSensorList(Sensor.TYPE_ALL);
|
||||
// for (Sensor sensor : s)
|
||||
// log.debug(sensor.toString());
|
||||
|
||||
mArrowView = (ImageView) App.activity.findViewById(R.id.compass);
|
||||
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
public synchronized float getRotation() {
|
||||
return mCurRotation;
|
||||
}
|
||||
|
||||
public void controlView(boolean enable) {
|
||||
mControlOrientation = enable;
|
||||
}
|
||||
|
||||
public boolean controlView() {
|
||||
return mControlOrientation;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
if (mode == mMode)
|
||||
return;
|
||||
|
||||
if (mode == Mode.OFF) {
|
||||
setEnabled(false);
|
||||
|
||||
mMap.getEventLayer().enableRotation(true);
|
||||
mMap.getEventLayer().enableTilt(true);
|
||||
} else if (mMode == Mode.OFF) {
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
if (mode == Mode.C3D) {
|
||||
mMap.getEventLayer().enableRotation(false);
|
||||
mMap.getEventLayer().enableTilt(false);
|
||||
} else if (mode == Mode.C2D) {
|
||||
mMap.getEventLayer().enableRotation(false);
|
||||
mMap.getEventLayer().enableTilt(true);
|
||||
}
|
||||
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
mListeners += enabled ? 1 : -1;
|
||||
|
||||
if (mListeners == 1) {
|
||||
resume();
|
||||
} else if (mListeners == 0) {
|
||||
pause();
|
||||
|
||||
} else if (mListeners < 0) {
|
||||
// then bad
|
||||
mListeners = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (mListeners <= 0)
|
||||
return;
|
||||
|
||||
super.setEnabled(true);
|
||||
|
||||
Sensor sensor;
|
||||
// Sensor sensor =
|
||||
// mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
|
||||
// Sensor sensor =
|
||||
// mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
// sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
|
||||
// mSensorManager.registerListener(this, sensor,
|
||||
// SensorManager.SENSOR_DELAY_UI);
|
||||
// sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
|
||||
// mSensorManager.registerListener(this, sensor,
|
||||
// SensorManager.SENSOR_DELAY_UI);
|
||||
|
||||
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||
mSensorManager.registerListener(this, sensor,
|
||||
SensorManager.SENSOR_DELAY_UI);
|
||||
|
||||
// mLastAccelerometerSet = false;
|
||||
// mLastMagnetometerSet = false;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (mListeners <= 0)
|
||||
return;
|
||||
|
||||
super.setEnabled(false);
|
||||
mSensorManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
public void adjustArrow(float prev, float cur) {
|
||||
Animation an = new RotateAnimation(-prev,
|
||||
-cur,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f);
|
||||
|
||||
an.setDuration(100);
|
||||
an.setRepeatCount(0);
|
||||
an.setFillAfter(true);
|
||||
|
||||
mArrowView.startAnimation(an);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
|
||||
if (event.sensor.getType() != Sensor.TYPE_ORIENTATION)
|
||||
return;
|
||||
System.arraycopy(event.values, 0, mRotationV, 0, event.values.length);
|
||||
|
||||
// SensorManager.getRotationMatrixFromVector(mRotationM, event.values);
|
||||
// SensorManager.getOrientation(mRotationM, mRotationV);
|
||||
|
||||
// int type = event.sensor.getType();
|
||||
// if (type == Sensor.TYPE_ACCELEROMETER) {
|
||||
// System.arraycopy(event.values, 0, mAccelV, 0, event.values.length);
|
||||
// mLastAccelerometerSet = true;
|
||||
// } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
|
||||
// System.arraycopy(event.values, 0, mMagnetV, 0, event.values.length);
|
||||
// mLastMagnetometerSet = true;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// if (!mLastAccelerometerSet || !mLastMagnetometerSet)
|
||||
// return;
|
||||
|
||||
// SensorManager.getRotationMatrix(mRotationM, null, mAccelV, mMagnetV);
|
||||
// SensorManager.getOrientation(mRotationM, mRotationV);
|
||||
|
||||
// float rotation = (float) Math.toDegrees(mRotationV[0]);
|
||||
float rotation = mRotationV[0];
|
||||
|
||||
// handle(event);
|
||||
// if (!mOrientationOK)
|
||||
// return;
|
||||
// float rotation = (float) Math.toDegrees(mAzimuthRadians);
|
||||
|
||||
float change = rotation - mCurRotation;
|
||||
if (change > 180)
|
||||
change -= 360;
|
||||
else if (change < -180)
|
||||
change += 360;
|
||||
|
||||
// low-pass
|
||||
change *= 0.05;
|
||||
|
||||
rotation = mCurRotation + change;
|
||||
|
||||
if (rotation > 180)
|
||||
rotation -= 360;
|
||||
else if (rotation < -180)
|
||||
rotation += 360;
|
||||
|
||||
// float tilt = (float) Math.toDegrees(mRotationV[1]);
|
||||
// float tilt = (float) Math.toDegrees(mPitchAxisRadians);
|
||||
float tilt = mRotationV[1];
|
||||
|
||||
mCurTilt = mCurTilt + 0.2f * (tilt - mCurTilt);
|
||||
|
||||
if (mMode != Mode.OFF) {
|
||||
boolean redraw = false;
|
||||
|
||||
if (Math.abs(change) > 0.01) {
|
||||
adjustArrow(mCurRotation, rotation);
|
||||
mMap.viewport().setRotation(-rotation);
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (mMode == Mode.C3D)
|
||||
redraw |= mMap.viewport().setTilt(-mCurTilt * 1.5f);
|
||||
|
||||
if (redraw)
|
||||
mMap.updateMap(true);
|
||||
}
|
||||
mCurRotation = rotation;
|
||||
}
|
||||
|
||||
// from http://stackoverflow.com/questions/16317599/android-compass-that-
|
||||
// can-compensate-for-tilt-and-pitch/16386066#16386066
|
||||
|
||||
// private int mGravityAccuracy;
|
||||
// private int mMagneticFieldAccuracy;
|
||||
|
||||
// private float[] mGravityV = new float[3];
|
||||
// private float[] mMagFieldV = new float[3];
|
||||
// private float[] mEastV = new float[3];
|
||||
// private float[] mNorthV = new float[3];
|
||||
//
|
||||
// private float mNormGravity;
|
||||
// private float mNormMagField;
|
||||
//
|
||||
// private boolean mOrientationOK;
|
||||
// private float mAzimuthRadians;
|
||||
// private float mPitchRadians;
|
||||
// private float mPitchAxisRadians;
|
||||
//
|
||||
// private void handle(SensorEvent event) {
|
||||
// int SensorType = event.sensor.getType();
|
||||
// switch (SensorType) {
|
||||
// case Sensor.TYPE_GRAVITY:
|
||||
// mLastAccelerometerSet = true;
|
||||
// System.arraycopy(event.values, 0, mGravityV, 0, mGravityV.length);
|
||||
// mNormGravity = (float) Math.sqrt(mGravityV[0] * mGravityV[0]
|
||||
// + mGravityV[1] * mGravityV[1] + mGravityV[2]
|
||||
// * mGravityV[2]);
|
||||
// for (int i = 0; i < mGravityV.length; i++)
|
||||
// mGravityV[i] /= mNormGravity;
|
||||
// break;
|
||||
// case Sensor.TYPE_MAGNETIC_FIELD:
|
||||
// mLastMagnetometerSet = true;
|
||||
// System.arraycopy(event.values, 0, mMagFieldV, 0, mMagFieldV.length);
|
||||
// mNormMagField = (float) Math.sqrt(mMagFieldV[0] * mMagFieldV[0]
|
||||
// + mMagFieldV[1] * mMagFieldV[1] + mMagFieldV[2]
|
||||
// * mMagFieldV[2]);
|
||||
// for (int i = 0; i < mMagFieldV.length; i++)
|
||||
// mMagFieldV[i] /= mNormMagField;
|
||||
// break;
|
||||
// }
|
||||
// if (!mLastAccelerometerSet || !mLastMagnetometerSet)
|
||||
// return;
|
||||
//
|
||||
// // first calculate the horizontal vector that points due east
|
||||
// float ex = mMagFieldV[1] * mGravityV[2] - mMagFieldV[2] * mGravityV[1];
|
||||
// float ey = mMagFieldV[2] * mGravityV[0] - mMagFieldV[0] * mGravityV[2];
|
||||
// float ez = mMagFieldV[0] * mGravityV[1] - mMagFieldV[1] * mGravityV[0];
|
||||
// float normEast = (float) Math.sqrt(ex * ex + ey * ey + ez * ez);
|
||||
//
|
||||
// if (mNormGravity * mNormMagField * normEast < 0.1f) { // Typical values
|
||||
// are > 100.
|
||||
// // device is close to free fall (or in space?), or close to magnetic
|
||||
// north pole.
|
||||
// mOrientationOK = false;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// mEastV[0] = ex / normEast;
|
||||
// mEastV[1] = ey / normEast;
|
||||
// mEastV[2] = ez / normEast;
|
||||
//
|
||||
// // next calculate the horizontal vector that points due north
|
||||
// float mdotG = (mGravityV[0] * mMagFieldV[0]
|
||||
// + mGravityV[1] * mMagFieldV[1]
|
||||
// + mGravityV[2] * mMagFieldV[2]);
|
||||
//
|
||||
// float nx = mMagFieldV[0] - mGravityV[0] * mdotG;
|
||||
// float ny = mMagFieldV[1] - mGravityV[1] * mdotG;
|
||||
// float nz = mMagFieldV[2] - mGravityV[2] * mdotG;
|
||||
// float normNorth = (float) Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
//
|
||||
// mNorthV[0] = nx / normNorth;
|
||||
// mNorthV[1] = ny / normNorth;
|
||||
// mNorthV[2] = nz / normNorth;
|
||||
//
|
||||
// // take account of screen rotation away from its natural rotation
|
||||
// //int rotation =
|
||||
// App.activity.getWindowManager().getDefaultDisplay().getRotation();
|
||||
// float screenDirection = 0;
|
||||
// //switch(rotation) {
|
||||
// // case Surface.ROTATION_0: screenDirection = 0; break;
|
||||
// // case Surface.ROTATION_90: screenDirection = (float)Math.PI/2; break;
|
||||
// // case Surface.ROTATION_180: screenDirection = (float)Math.PI; break;
|
||||
// // case Surface.ROTATION_270: screenDirection = 3*(float)Math.PI/2;
|
||||
// break;
|
||||
// //}
|
||||
// // NB: the rotation matrix has now effectively been calculated. It
|
||||
// consists of
|
||||
// // the three vectors mEastV[], mNorthV[] and mGravityV[]
|
||||
//
|
||||
// // calculate all the required angles from the rotation matrix
|
||||
// // NB: see
|
||||
// http://math.stackexchange.com/questions/381649/whats-the-best-3d-angular-
|
||||
// // co-ordinate-system-for-working-with-smartfone-apps
|
||||
// float sin = mEastV[1] - mNorthV[0], cos = mEastV[0] + mNorthV[1];
|
||||
// mAzimuthRadians = (float) (sin != 0 && cos != 0 ? Math.atan2(sin, cos) :
|
||||
// 0);
|
||||
// mPitchRadians = (float) Math.acos(mGravityV[2]);
|
||||
//
|
||||
// sin = -mEastV[1] - mNorthV[0];
|
||||
// cos = mEastV[0] - mNorthV[1];
|
||||
//
|
||||
// float aximuthPlusTwoPitchAxisRadians =
|
||||
// (float) (sin != 0 && cos != 0 ? Math.atan2(sin, cos) : 0);
|
||||
//
|
||||
// mPitchAxisRadians = (float) (aximuthPlusTwoPitchAxisRadians -
|
||||
// mAzimuthRadians) / 2;
|
||||
// mAzimuthRadians += screenDirection;
|
||||
// mPitchAxisRadians += screenDirection;
|
||||
//
|
||||
// mOrientationOK = true;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// int type = sensor.getType();
|
||||
// switch (type) {
|
||||
// case Sensor.TYPE_GRAVITY:
|
||||
// mGravityAccuracy = accuracy;
|
||||
// break;
|
||||
// case Sensor.TYPE_MAGNETIC_FIELD:
|
||||
// mMagneticFieldAccuracy = accuracy;
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
120
vtm-app/src/org/oscim/app/location/LocationDialog.java
Normal file
120
vtm-app/src/org/oscim/app/location/LocationDialog.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
* Copyright 2016 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.app.location;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.app.TileMap;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.map.Map;
|
||||
|
||||
public class LocationDialog {
|
||||
|
||||
public void prepareDialog(Map map, final Dialog dialog) {
|
||||
EditText editText = (EditText) dialog.findViewById(R.id.latitude);
|
||||
|
||||
MapPosition mapCenter = map.getMapPosition();
|
||||
|
||||
editText.setText(Double.toString(mapCenter.getLatitude()));
|
||||
|
||||
editText = (EditText) dialog.findViewById(R.id.longitude);
|
||||
editText.setText(Double.toString(mapCenter.getLongitude()));
|
||||
|
||||
SeekBar zoomlevel = (SeekBar) dialog.findViewById(R.id.zoomLevel);
|
||||
zoomlevel.setMax(20);
|
||||
zoomlevel.setProgress(10);
|
||||
|
||||
final TextView textView = (TextView) dialog.findViewById(R.id.zoomlevelValue);
|
||||
textView.setText(String.valueOf(zoomlevel.getProgress()));
|
||||
zoomlevel.setOnSeekBarChangeListener(new SeekBarChangeListener(textView));
|
||||
}
|
||||
|
||||
public Dialog createDialog(final TileMap map) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(map);
|
||||
builder.setIcon(android.R.drawable.ic_menu_mylocation);
|
||||
builder.setTitle(R.string.menu_position_enter_coordinates);
|
||||
LayoutInflater factory = LayoutInflater.from(map);
|
||||
final View view = factory.inflate(R.layout.dialog_enter_coordinates, null);
|
||||
builder.setView(view);
|
||||
|
||||
builder.setPositiveButton(R.string.go_to_position,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// disable GPS follow mode if it is enabled
|
||||
//map.mLocation.disableSnapToLocation();
|
||||
if (map.getLocationHandler().getMode() == LocationHandler.Mode.SNAP)
|
||||
map.getLocationHandler()
|
||||
.setMode(LocationHandler.Mode.SHOW);
|
||||
|
||||
// set the map center and zoom level
|
||||
EditText latitudeView = (EditText) view
|
||||
.findViewById(R.id.latitude);
|
||||
EditText longitudeView = (EditText) view
|
||||
.findViewById(R.id.longitude);
|
||||
double latitude = Double.parseDouble(latitudeView.getText()
|
||||
.toString());
|
||||
double longitude = Double.parseDouble(longitudeView.getText()
|
||||
.toString());
|
||||
|
||||
SeekBar zoomLevelView = (SeekBar) view
|
||||
.findViewById(R.id.zoomLevel);
|
||||
|
||||
int zoom = zoomLevelView.getProgress();
|
||||
|
||||
MapPosition mapPosition = new MapPosition();
|
||||
mapPosition.setPosition(latitude, longitude);
|
||||
mapPosition.setZoomLevel(zoom);
|
||||
App.map.setMapPosition(mapPosition);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
private final TextView textView;
|
||||
|
||||
SeekBarChangeListener(TextView textView) {
|
||||
this.textView = textView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
this.textView.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
236
vtm-app/src/org/oscim/app/location/LocationHandler.java
Normal file
236
vtm-app/src/org/oscim/app/location/LocationHandler.java
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2013 Ahmad Al-saleem
|
||||
*
|
||||
* 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.app.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.app.TileMap;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LocationHandler implements LocationListener {
|
||||
final static Logger log = LoggerFactory.getLogger(LocationHandler.class);
|
||||
|
||||
public enum Mode {
|
||||
OFF,
|
||||
SHOW,
|
||||
SNAP,
|
||||
}
|
||||
|
||||
private final static int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
|
||||
private final static int SHOW_LOCATION_ZOOM = 14;
|
||||
|
||||
private final LocationManager mLocationManager;
|
||||
private final LocationOverlay mLocationOverlay;
|
||||
|
||||
private Mode mMode = Mode.OFF;
|
||||
|
||||
private boolean mSetCenter;
|
||||
private MapPosition mMapPosition;
|
||||
|
||||
public LocationHandler(TileMap tileMap, Compass compass) {
|
||||
mLocationManager = (LocationManager) tileMap
|
||||
.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
mLocationOverlay = new LocationOverlay(App.map, compass);
|
||||
|
||||
mMapPosition = new MapPosition();
|
||||
}
|
||||
|
||||
public boolean setMode(Mode mode) {
|
||||
if (mode == mMode)
|
||||
return true;
|
||||
|
||||
if (mode == Mode.OFF) {
|
||||
disableShowMyLocation();
|
||||
|
||||
if (mMode == Mode.SNAP)
|
||||
App.map.getEventLayer().enableMove(true);
|
||||
}
|
||||
|
||||
if (mMode == Mode.OFF) {
|
||||
if (!enableShowMyLocation())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode == Mode.SNAP) {
|
||||
App.map.getEventLayer().enableMove(false);
|
||||
gotoLastKnownPosition();
|
||||
} else {
|
||||
App.map.getEventLayer().enableMove(true);
|
||||
}
|
||||
|
||||
// FIXME?
|
||||
mSetCenter = false;
|
||||
mMode = mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mMode;
|
||||
}
|
||||
|
||||
public boolean isFirstCenter() {
|
||||
return mSetCenter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean enableShowMyLocation() {
|
||||
|
||||
Criteria criteria = new Criteria();
|
||||
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||
String bestProvider = mLocationManager.getBestProvider(criteria, true);
|
||||
|
||||
if (bestProvider == null) {
|
||||
App.activity.showDialog(DIALOG_LOCATION_PROVIDER_DISABLED);
|
||||
return false;
|
||||
}
|
||||
|
||||
mLocationManager.requestLocationUpdates(bestProvider, 10000, 10, this);
|
||||
|
||||
Location location = gotoLastKnownPosition();
|
||||
if (location == null)
|
||||
return false;
|
||||
|
||||
mLocationOverlay.setEnabled(true);
|
||||
mLocationOverlay.setPosition(location.getLatitude(),
|
||||
location.getLongitude(),
|
||||
location.getAccuracy());
|
||||
|
||||
// FIXME -> implement LayerGroup
|
||||
App.map.layers().add(4, mLocationOverlay);
|
||||
|
||||
App.map.updateMap(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable "show my location" mode.
|
||||
*/
|
||||
private boolean disableShowMyLocation() {
|
||||
|
||||
mLocationManager.removeUpdates(this);
|
||||
mLocationOverlay.setEnabled(false);
|
||||
|
||||
App.map.layers().remove(mLocationOverlay);
|
||||
App.map.updateMap(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Location gotoLastKnownPosition() {
|
||||
Location location = null;
|
||||
float bestAccuracy = Float.MAX_VALUE;
|
||||
|
||||
for (String provider : mLocationManager.getProviders(true)) {
|
||||
Location l = mLocationManager.getLastKnownLocation(provider);
|
||||
if (l == null)
|
||||
continue;
|
||||
|
||||
float accuracy = l.getAccuracy();
|
||||
if (accuracy <= 0)
|
||||
accuracy = Float.MAX_VALUE;
|
||||
|
||||
if (location == null || accuracy <= bestAccuracy) {
|
||||
location = l;
|
||||
bestAccuracy = accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
if (location == null) {
|
||||
App.activity.showToastOnUiThread(App.activity
|
||||
.getString(R.string.error_last_location_unknown));
|
||||
return null;
|
||||
}
|
||||
|
||||
App.map.getMapPosition(mMapPosition);
|
||||
|
||||
if (mMapPosition.zoomLevel < SHOW_LOCATION_ZOOM)
|
||||
mMapPosition.setZoomLevel(SHOW_LOCATION_ZOOM);
|
||||
|
||||
mMapPosition.setPosition(location.getLatitude(), location.getLongitude());
|
||||
App.map.setMapPosition(mMapPosition);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
/***
|
||||
* LocationListener
|
||||
***/
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
|
||||
if (mMode == Mode.OFF)
|
||||
return;
|
||||
|
||||
double lat = location.getLatitude();
|
||||
double lon = location.getLongitude();
|
||||
|
||||
log.debug("update location " + lat + ":" + lon);
|
||||
|
||||
if (mSetCenter || mMode == Mode.SNAP) {
|
||||
mSetCenter = false;
|
||||
|
||||
App.map.getMapPosition(mMapPosition);
|
||||
mMapPosition.setPosition(lat, lon);
|
||||
App.map.setMapPosition(mMapPosition);
|
||||
}
|
||||
|
||||
mLocationOverlay.setPosition(lat, lon, location.getAccuracy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
public void setCenterOnFirstFix() {
|
||||
mSetCenter = true;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (mMode != Mode.OFF) {
|
||||
log.debug("pause location listener");
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (mMode != Mode.OFF) {
|
||||
Criteria criteria = new Criteria();
|
||||
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||
String bestProvider = mLocationManager.getBestProvider(criteria, true);
|
||||
mLocationManager.requestLocationUpdates(bestProvider, 10000, 10, this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
331
vtm-app/src/org/oscim/app/location/LocationOverlay.java
Normal file
331
vtm-app/src/org/oscim/app/location/LocationOverlay.java
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright 2013 Ahmad Saleem
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 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.app.location;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import org.oscim.backend.GL;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Point;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.renderer.GLShader;
|
||||
import org.oscim.renderer.GLState;
|
||||
import org.oscim.renderer.GLViewport;
|
||||
import org.oscim.renderer.LayerRenderer;
|
||||
import org.oscim.renderer.MapRenderer;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.utils.math.Interpolation;
|
||||
|
||||
import static org.oscim.backend.GLAdapter.gl;
|
||||
|
||||
public class LocationOverlay extends Layer {
|
||||
private final int SHOW_ACCURACY_ZOOM = 16;
|
||||
|
||||
private final Point mLocation = new Point();
|
||||
private double mRadius;
|
||||
|
||||
private final Compass mCompass;
|
||||
|
||||
public LocationOverlay(Map map, Compass compass) {
|
||||
super(map);
|
||||
mRenderer = new LocationIndicator(map);
|
||||
mCompass = compass;
|
||||
}
|
||||
|
||||
public void setPosition(double latitude, double longitude, double accuracy) {
|
||||
mLocation.x = MercatorProjection.longitudeToX(longitude);
|
||||
mLocation.y = MercatorProjection.latitudeToY(latitude);
|
||||
mRadius = accuracy / MercatorProjection.groundResolution(latitude, 1);
|
||||
((LocationIndicator) mRenderer).animate(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (enabled == isEnabled())
|
||||
return;
|
||||
|
||||
super.setEnabled(enabled);
|
||||
|
||||
if (!enabled)
|
||||
((LocationIndicator) mRenderer).animate(false);
|
||||
|
||||
mCompass.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public class LocationIndicator extends LayerRenderer {
|
||||
private int mShaderProgram;
|
||||
private int hVertexPosition;
|
||||
private int hMatrixPosition;
|
||||
private int hScale;
|
||||
private int hPhase;
|
||||
private int hDirection;
|
||||
|
||||
private final float CIRCLE_SIZE = 60;
|
||||
|
||||
private final static long ANIM_RATE = 50;
|
||||
private final static long INTERVAL = 2000;
|
||||
|
||||
private final Point mIndicatorPosition = new Point();
|
||||
|
||||
private final Point mScreenPoint = new Point();
|
||||
private final Box mBBox = new Box();
|
||||
|
||||
private boolean mInitialized;
|
||||
|
||||
private boolean mLocationIsVisible;
|
||||
|
||||
private boolean mRunAnim;
|
||||
private long mAnimStart;
|
||||
|
||||
public LocationIndicator(final Map map) {
|
||||
super();
|
||||
}
|
||||
|
||||
private void animate(boolean enable) {
|
||||
if (mRunAnim == enable)
|
||||
return;
|
||||
|
||||
mRunAnim = enable;
|
||||
if (!enable)
|
||||
return;
|
||||
|
||||
final Runnable action = new Runnable() {
|
||||
private long lastRun;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mRunAnim)
|
||||
return;
|
||||
|
||||
long diff = SystemClock.elapsedRealtime() - lastRun;
|
||||
mMap.postDelayed(this, Math.min(ANIM_RATE, diff));
|
||||
mMap.render();
|
||||
}
|
||||
};
|
||||
|
||||
mAnimStart = SystemClock.elapsedRealtime();
|
||||
mMap.postDelayed(action, ANIM_RATE);
|
||||
}
|
||||
|
||||
private float animPhase() {
|
||||
return (float) ((MapRenderer.frametime - mAnimStart) % INTERVAL) / INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GLViewport v) {
|
||||
|
||||
if (!mInitialized) {
|
||||
init();
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
if (!isEnabled()) {
|
||||
setReady(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!v.changed() && isReady())
|
||||
return;
|
||||
|
||||
setReady(true);
|
||||
|
||||
int width = mMap.getWidth();
|
||||
int height = mMap.getHeight();
|
||||
|
||||
// clamp location to a position that can be
|
||||
// savely translated to screen coordinates
|
||||
v.getBBox(mBBox, 0);
|
||||
|
||||
double x = mLocation.x;
|
||||
double y = mLocation.y;
|
||||
|
||||
if (!mBBox.contains(mLocation)) {
|
||||
x = FastMath.clamp(x, mBBox.xmin, mBBox.xmax);
|
||||
y = FastMath.clamp(y, mBBox.ymin, mBBox.ymax);
|
||||
}
|
||||
|
||||
// get position of Location in pixel relative to
|
||||
// screen center
|
||||
v.toScreenPoint(x, y, mScreenPoint);
|
||||
|
||||
x = mScreenPoint.x + width / 2;
|
||||
y = mScreenPoint.y + height / 2;
|
||||
|
||||
// clip position to screen boundaries
|
||||
int visible = 0;
|
||||
|
||||
if (x > width - 5)
|
||||
x = width;
|
||||
else if (x < 5)
|
||||
x = 0;
|
||||
else
|
||||
visible++;
|
||||
|
||||
if (y > height - 5)
|
||||
y = height;
|
||||
else if (y < 5)
|
||||
y = 0;
|
||||
else
|
||||
visible++;
|
||||
|
||||
mLocationIsVisible = (visible == 2);
|
||||
|
||||
// set location indicator position
|
||||
v.fromScreenPoint(x, y, mIndicatorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GLViewport v) {
|
||||
|
||||
GLState.useProgram(mShaderProgram);
|
||||
GLState.blend(true);
|
||||
GLState.test(false, false);
|
||||
|
||||
GLState.enableVertexArrays(hVertexPosition, -1);
|
||||
MapRenderer.bindQuadVertexVBO(hVertexPosition/*, true*/);
|
||||
|
||||
float radius = CIRCLE_SIZE;
|
||||
|
||||
animate(true);
|
||||
boolean viewShed = false;
|
||||
if (!mLocationIsVisible /* || pos.zoomLevel < SHOW_ACCURACY_ZOOM */) {
|
||||
//animate(true);
|
||||
} else {
|
||||
if (v.pos.zoomLevel >= SHOW_ACCURACY_ZOOM)
|
||||
radius = (float) (mRadius * v.pos.scale);
|
||||
|
||||
viewShed = true;
|
||||
//animate(false);
|
||||
}
|
||||
gl.uniform1f(hScale, radius);
|
||||
|
||||
double x = mIndicatorPosition.x - v.pos.x;
|
||||
double y = mIndicatorPosition.y - v.pos.y;
|
||||
double tileScale = Tile.SIZE * v.pos.scale;
|
||||
|
||||
v.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1);
|
||||
v.mvp.multiplyMM(v.viewproj, v.mvp);
|
||||
v.mvp.setAsUniform(hMatrixPosition);
|
||||
|
||||
if (!viewShed) {
|
||||
float phase = Math.abs(animPhase() - 0.5f) * 2;
|
||||
//phase = Interpolation.fade.apply(phase);
|
||||
phase = Interpolation.swing.apply(phase);
|
||||
|
||||
gl.uniform1f(hPhase, 0.8f + phase * 0.2f);
|
||||
} else {
|
||||
gl.uniform1f(hPhase, 1);
|
||||
}
|
||||
|
||||
if (viewShed && mLocationIsVisible) {
|
||||
float rotation = mCompass.getRotation() - 90;
|
||||
gl.uniform2f(hDirection,
|
||||
(float) Math.cos(Math.toRadians(rotation)),
|
||||
(float) Math.sin(Math.toRadians(rotation)));
|
||||
} else {
|
||||
gl.uniform2f(hDirection, 0, 0);
|
||||
}
|
||||
|
||||
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
private boolean init() {
|
||||
int shader = GLShader.createProgram(vShaderStr, fShaderStr);
|
||||
if (shader == 0)
|
||||
return false;
|
||||
|
||||
mShaderProgram = shader;
|
||||
hVertexPosition = gl.getAttribLocation(shader, "a_pos");
|
||||
hMatrixPosition = gl.getUniformLocation(shader, "u_mvp");
|
||||
hPhase = gl.getUniformLocation(shader, "u_phase");
|
||||
hScale = gl.getUniformLocation(shader, "u_scale");
|
||||
hDirection = gl.getUniformLocation(shader, "u_dir");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final static String vShaderStr = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform mat4 u_mvp;"
|
||||
+ "uniform float u_phase;"
|
||||
+ "uniform float u_scale;"
|
||||
+ "attribute vec2 a_pos;"
|
||||
+ "varying vec2 v_tex;"
|
||||
+ "void main() {"
|
||||
+ " gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0);"
|
||||
+ " v_tex = a_pos;"
|
||||
+ "}";
|
||||
|
||||
private final static String fShaderStr = ""
|
||||
+ "precision mediump float;"
|
||||
+ "varying vec2 v_tex;"
|
||||
+ "uniform float u_scale;"
|
||||
+ "uniform float u_phase;"
|
||||
+ "uniform vec2 u_dir;"
|
||||
|
||||
+ "void main() {"
|
||||
+ " float len = 1.0 - length(v_tex);"
|
||||
+ " if (u_dir.x == 0.0 && u_dir.y == 0.0){"
|
||||
+ " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * len;"
|
||||
+ " } else {"
|
||||
/// outer ring
|
||||
+ " float a = smoothstep(0.0, 2.0 / u_scale, len);"
|
||||
/// inner ring
|
||||
+ " float b = 0.5 * smoothstep(4.0 / u_scale, 5.0 / u_scale, len);"
|
||||
/// center point
|
||||
+ " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
|
||||
+ " vec2 dir = normalize(v_tex);"
|
||||
+ " float d = 1.0 - dot(dir, u_dir); "
|
||||
/// 0.5 width of viewshed
|
||||
+ " d = clamp(step(0.5, d), 0.4, 0.7);"
|
||||
/// - subtract inner from outer to create the outline
|
||||
/// - multiply by viewshed
|
||||
/// - add center point
|
||||
+ " a = d * (a - (b + c)) + c;"
|
||||
+ " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
|
||||
+ "}}";
|
||||
|
||||
//private final static String fShaderStr = ""
|
||||
// + "precision mediump float;"
|
||||
// + "varying vec2 v_tex;"
|
||||
// + "uniform float u_scale;"
|
||||
// + "uniform float u_phase;"
|
||||
// + "uniform vec2 u_dir;"
|
||||
// + "void main() {"
|
||||
// + " float len = 1.0 - length(v_tex);"
|
||||
// /// outer ring
|
||||
// + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
|
||||
// /// inner ring
|
||||
// + " float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len);"
|
||||
// /// center point
|
||||
// + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
|
||||
// + " vec2 dir = normalize(v_tex);"
|
||||
// + " float d = dot(dir, u_dir); "
|
||||
// /// 0.5 width of viewshed
|
||||
// + " d = clamp(smoothstep(0.7, 0.7 + 2.0/u_scale, d) * len, 0.0, 1.0);"
|
||||
// /// - subtract inner from outer to create the outline
|
||||
// /// - multiply by viewshed
|
||||
// /// - add center point
|
||||
// + " a = max(d, (a - (b + c)) + c);"
|
||||
// + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
|
||||
// + "}";
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user