Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54b7884fa3 | ||
|
|
e5bc7c7564 | ||
|
|
cf49591d4f | ||
|
|
71f94f3f79 | ||
|
|
db6be82ce2 | ||
|
|
ba65786b5a | ||
|
|
036d63c9dd | ||
|
|
07e86d24f6 |
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 0.11.1 (2019-04-12)
|
||||||
|
|
||||||
|
- MBTiles raster tile source (Android) [#708](https://github.com/mapsforge/vtm/pull/708)
|
||||||
|
- Fix Android 4.2 crash [#713](https://github.com/mapsforge/vtm/issues/713)
|
||||||
|
- Many other minor improvements and bug fixes
|
||||||
|
- [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.11.1)
|
||||||
|
|
||||||
## Version 0.11.0 (2019-03-25)
|
## Version 0.11.0 (2019-03-25)
|
||||||
|
|
||||||
- Render themes: tag transform [#420](https://github.com/mapsforge/vtm/issues/420)
|
- Render themes: tag transform [#420](https://github.com/mapsforge/vtm/issues/420)
|
||||||
|
|||||||
@@ -91,6 +91,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MarkerOverlayActivity"
|
android:name=".MarkerOverlayActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize" />
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
<activity
|
||||||
|
android:name=".MBTilesBitmapTileActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MultiMapViewActivity"
|
android:name=".MultiMapViewActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize" />
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
|||||||
@@ -25,5 +25,8 @@
|
|||||||
<string name="search_key">\'*\' or OSM key</string>
|
<string name="search_key">\'*\' or OSM key</string>
|
||||||
<string name="search_value">void or value</string>
|
<string name="search_value">void or value</string>
|
||||||
<string name="now">Now</string>
|
<string name="now">Now</string>
|
||||||
|
<string name="warning">Warning</string>
|
||||||
|
<string name="startup_message_mbtiles">To run this sample activity, you need any MBTiles with filename test.mbtiles installed on storage.\n\nadb push file.mbtiles /sdcard/test.mbtiles</string>
|
||||||
|
<string name="exit">Exit</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Andrea Antonello
|
||||||
|
* Copyright 2019 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.android.test;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import org.oscim.android.tiling.source.mbtiles.MBTilesBitmapTileSource;
|
||||||
|
import org.oscim.core.BoundingBox;
|
||||||
|
import org.oscim.core.MapPosition;
|
||||||
|
import org.oscim.core.Tile;
|
||||||
|
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example activity making use of MBTiles.
|
||||||
|
*/
|
||||||
|
public class MBTilesBitmapTileActivity extends BitmapTileActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
File file = new File(Environment.getExternalStorageDirectory(), "test.mbtiles");
|
||||||
|
if (!file.exists()) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
||||||
|
.setTitle(R.string.warning)
|
||||||
|
.setMessage(R.string.startup_message_mbtiles)
|
||||||
|
.setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MBTilesBitmapTileSource tileSource = new MBTilesBitmapTileSource(file.getAbsolutePath(), 128, null);
|
||||||
|
BitmapTileLayer bitmapLayer = new BitmapTileLayer(mMap, tileSource);
|
||||||
|
mMap.layers().add(bitmapLayer);
|
||||||
|
|
||||||
|
/* set initial position on first run */
|
||||||
|
MapPosition pos = new MapPosition();
|
||||||
|
mMap.getMapPosition(pos);
|
||||||
|
if (pos.x == 0.5 && pos.y == 0.5) {
|
||||||
|
BoundingBox bbox = tileSource.getBounds();
|
||||||
|
if (bbox != null) {
|
||||||
|
pos.setByBoundingBox(bbox, Tile.SIZE * 4, Tile.SIZE * 4);
|
||||||
|
mMap.setMapPosition(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
* Copyright 2017 nebular
|
* Copyright 2017 nebular
|
||||||
* Copyright 2018 boldtrn
|
* Copyright 2018 boldtrn
|
||||||
* Copyright 2018-2019 Gustl22
|
* Copyright 2018-2019 Gustl22
|
||||||
|
* Copyright 2019 Andrea Antonello
|
||||||
*
|
*
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||||
*
|
*
|
||||||
@@ -115,6 +116,7 @@ public class Samples extends Activity {
|
|||||||
|
|
||||||
linearLayout.addView(createLabel("Raster Maps"));
|
linearLayout.addView(createLabel("Raster Maps"));
|
||||||
linearLayout.addView(createButton(BitmapTileActivity.class));
|
linearLayout.addView(createButton(BitmapTileActivity.class));
|
||||||
|
linearLayout.addView(createButton(MBTilesBitmapTileActivity.class));
|
||||||
|
|
||||||
linearLayout.addView(createLabel("Overlays"));
|
linearLayout.addView(createLabel("Overlays"));
|
||||||
linearLayout.addView(createButton(MarkerOverlayActivity.class));
|
linearLayout.addView(createButton(MarkerOverlayActivity.class));
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import android.util.DisplayMetrics;
|
|||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
import org.oscim.android.canvas.AndroidGraphics;
|
import org.oscim.android.canvas.AndroidGraphics;
|
||||||
import org.oscim.android.gl.AndroidGL;
|
import org.oscim.android.gl.AndroidGL;
|
||||||
import org.oscim.android.gl.AndroidGL30;
|
import org.oscim.android.gl.AndroidGL30;
|
||||||
@@ -47,11 +46,10 @@ import org.oscim.utils.Parameters;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.microedition.khronos.egl.EGLConfig;
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The MapView,
|
* The MapView,
|
||||||
@@ -127,12 +125,16 @@ public class MapView extends GLSurfaceView {
|
|||||||
mMap = new AndroidMap(this);
|
mMap = new AndroidMap(this);
|
||||||
|
|
||||||
/* Initialize Renderer */
|
/* Initialize Renderer */
|
||||||
try {
|
// OpenGL ES 3.0 is supported with Android 4.3 (API level 18) and higher
|
||||||
setEGLContextFactory(new GlContextFactory());
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
} catch (Throwable t) {
|
try {
|
||||||
log.error("Falling back to GLES 2", t);
|
setEGLContextFactory(new GlContextFactory());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.error("Falling back to GLES 2", t);
|
||||||
|
setEGLContextClientVersion(2);
|
||||||
|
}
|
||||||
|
} else
|
||||||
setEGLContextClientVersion(2);
|
setEGLContextClientVersion(2);
|
||||||
}
|
|
||||||
setEGLConfigChooser(new GlConfigChooser());
|
setEGLConfigChooser(new GlConfigChooser());
|
||||||
|
|
||||||
if (GLAdapter.debug)
|
if (GLAdapter.debug)
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Andrea Antonello
|
||||||
|
* Copyright 2019 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.android.tiling.source.mbtiles;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import org.oscim.backend.CanvasAdapter;
|
||||||
|
import org.oscim.backend.canvas.Bitmap;
|
||||||
|
import org.oscim.core.BoundingBox;
|
||||||
|
import org.oscim.core.MercatorProjection;
|
||||||
|
import org.oscim.layers.tile.MapTile;
|
||||||
|
import org.oscim.map.Viewport;
|
||||||
|
import org.oscim.tiling.ITileDataSink;
|
||||||
|
import org.oscim.tiling.ITileDataSource;
|
||||||
|
import org.oscim.tiling.QueryResult;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tile data source for MBTiles raster databases.
|
||||||
|
*/
|
||||||
|
public class MBTilesBitmapTileDataSource implements ITileDataSource {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MBTilesBitmapTileDataSource.class);
|
||||||
|
|
||||||
|
private static final String TABLE_TILES = "tiles";
|
||||||
|
private static final String COL_TILES_ZOOM_LEVEL = "zoom_level";
|
||||||
|
private static final String COL_TILES_TILE_COLUMN = "tile_column";
|
||||||
|
private static final String COL_TILES_TILE_ROW = "tile_row";
|
||||||
|
private static final String COL_TILES_TILE_DATA = "tile_data";
|
||||||
|
private static final String SELECT_TILES = "SELECT " + COL_TILES_TILE_DATA + " from " + TABLE_TILES + " where "
|
||||||
|
+ COL_TILES_ZOOM_LEVEL + "=? AND " + COL_TILES_TILE_COLUMN + "=? AND " + COL_TILES_TILE_ROW + "=?";
|
||||||
|
|
||||||
|
private static final String TABLE_METADATA = "metadata";
|
||||||
|
private static final String COL_METADATA_NAME = "name";
|
||||||
|
private static final String COL_METADATA_VALUE = "value";
|
||||||
|
private static final String SELECT_METADATA = "select " + COL_METADATA_NAME + "," + COL_METADATA_VALUE + " from "
|
||||||
|
+ TABLE_METADATA;
|
||||||
|
|
||||||
|
private final Integer mAlpha;
|
||||||
|
private final SQLiteDatabase mDatabase;
|
||||||
|
private Map<String, String> mMetadata;
|
||||||
|
private final Integer mTransparentColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MBTiles tile data source.
|
||||||
|
*
|
||||||
|
* @param path the path to the MBTiles database.
|
||||||
|
* @param alpha an optional alpha value [0-255] to make the tiles transparent.
|
||||||
|
* @param transparentColor an optional color that will be made transparent in the bitmap.
|
||||||
|
*/
|
||||||
|
MBTilesBitmapTileDataSource(String path, Integer alpha, Integer transparentColor) {
|
||||||
|
mDatabase = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
|
||||||
|
mAlpha = alpha;
|
||||||
|
mTransparentColor = transparentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
mDatabase.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
mDatabase.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAttribution() {
|
||||||
|
return getMetadata().get("attribution");
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundingBox getBounds() {
|
||||||
|
String bounds = getMetadata().get("bounds");
|
||||||
|
if (bounds != null) {
|
||||||
|
String[] split = bounds.split(",");
|
||||||
|
double w = Double.parseDouble(split[0]);
|
||||||
|
double s = Double.parseDouble(split[1]);
|
||||||
|
double e = Double.parseDouble(split[2]);
|
||||||
|
double n = Double.parseDouble(split[3]);
|
||||||
|
return new BoundingBox(s, w, n, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return getMetadata().get("description");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the image format (jpg, png)
|
||||||
|
*/
|
||||||
|
public String getFormat() {
|
||||||
|
return getMetadata().get("format");
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMaxZoom() {
|
||||||
|
String maxZoom = getMetadata().get("maxzoom");
|
||||||
|
if (maxZoom != null)
|
||||||
|
return Integer.parseInt(maxZoom);
|
||||||
|
return Viewport.MAX_ZOOM_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getMetadata() {
|
||||||
|
if (mMetadata == null) {
|
||||||
|
mMetadata = new HashMap<>();
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = mDatabase.rawQuery(SELECT_METADATA, null);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String key = cursor.getString(0);
|
||||||
|
String value = cursor.getString(1);
|
||||||
|
mMetadata.put(key, value);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMinZoom() {
|
||||||
|
String minZoom = getMetadata().get("minzoom");
|
||||||
|
if (minZoom != null)
|
||||||
|
return Integer.parseInt(minZoom);
|
||||||
|
return Viewport.MIN_ZOOM_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return getMetadata().get("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return getMetadata().get("version");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static android.graphics.Bitmap processAlpha(android.graphics.Bitmap bitmap, int alpha) {
|
||||||
|
android.graphics.Bitmap newBitmap = android.graphics.Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), android.graphics.Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(newBitmap);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAlpha(alpha);
|
||||||
|
canvas.drawBitmap(bitmap, 0, 0, paint);
|
||||||
|
return newBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static android.graphics.Bitmap processTransparentColor(android.graphics.Bitmap bitmap, int colorToRemove) {
|
||||||
|
int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||||
|
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||||
|
for (int i = 0; i < pixels.length; i++) {
|
||||||
|
if (pixels[i] == colorToRemove)
|
||||||
|
pixels[i] = Color.alpha(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
android.graphics.Bitmap newBitmap = android.graphics.Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), android.graphics.Bitmap.Config.ARGB_8888);
|
||||||
|
newBitmap.setPixels(pixels, 0, newBitmap.getWidth(), 0, 0, newBitmap.getWidth(), newBitmap.getHeight());
|
||||||
|
return newBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void query(MapTile tile, ITileDataSink sink) {
|
||||||
|
QueryResult res = QueryResult.FAILED;
|
||||||
|
try {
|
||||||
|
byte[] bytes = readTile(tile.tileX, tile.tileY, tile.zoomLevel);
|
||||||
|
|
||||||
|
if (mTransparentColor != null || mAlpha != null) {
|
||||||
|
android.graphics.Bitmap androidBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||||
|
if (mTransparentColor != null)
|
||||||
|
androidBitmap = processTransparentColor(androidBitmap, mTransparentColor);
|
||||||
|
if (mAlpha != null)
|
||||||
|
androidBitmap = processAlpha(androidBitmap, mAlpha);
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
androidBitmap.compress(android.graphics.Bitmap.CompressFormat.PNG, 100, bos);
|
||||||
|
bytes = bos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmap = CanvasAdapter.decodeBitmap(new ByteArrayInputStream(bytes));
|
||||||
|
sink.setTileImage(bitmap);
|
||||||
|
res = QueryResult.SUCCESS;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("{} invalid bitmap", tile);
|
||||||
|
} finally {
|
||||||
|
sink.completed(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a Tile's image bytes from the MBTiles database.
|
||||||
|
*
|
||||||
|
* @param tileX the x tile index.
|
||||||
|
* @param tileY the y tile index (OSM notation).
|
||||||
|
* @param zoomLevel the zoom level.
|
||||||
|
* @return the tile image bytes.
|
||||||
|
*/
|
||||||
|
private byte[] readTile(int tileX, int tileY, byte zoomLevel) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
long tmsTileY = MercatorProjection.tileYToTMS(tileY, zoomLevel);
|
||||||
|
cursor = mDatabase.rawQuery(SELECT_TILES, new String[]{String.valueOf(zoomLevel), String.valueOf(tileX), String.valueOf(tmsTileY)});
|
||||||
|
if (cursor.moveToFirst())
|
||||||
|
return cursor.getBlob(0);
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Andrea Antonello
|
||||||
|
* Copyright 2019 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.android.tiling.source.mbtiles;
|
||||||
|
|
||||||
|
import org.oscim.core.BoundingBox;
|
||||||
|
import org.oscim.tiling.ITileDataSource;
|
||||||
|
import org.oscim.tiling.TileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tile source for MBTiles raster databases.
|
||||||
|
*/
|
||||||
|
public class MBTilesBitmapTileSource extends TileSource {
|
||||||
|
|
||||||
|
private final MBTilesBitmapTileDataSource mTileDataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MBTiles tile source.
|
||||||
|
*
|
||||||
|
* @param path the path to the MBTiles database.
|
||||||
|
*/
|
||||||
|
public MBTilesBitmapTileSource(String path) {
|
||||||
|
this(path, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MBTiles tile source.
|
||||||
|
*
|
||||||
|
* @param path the path to the MBTiles database.
|
||||||
|
* @param alpha an optional alpha value [0-255] to make the tiles transparent.
|
||||||
|
* @param transparentColor an optional color that will be made transparent in the bitmap.
|
||||||
|
*/
|
||||||
|
public MBTilesBitmapTileSource(String path, Integer alpha, Integer transparentColor) {
|
||||||
|
mTileDataSource = new MBTilesBitmapTileDataSource(path, alpha, transparentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mTileDataSource.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getAttribution() {
|
||||||
|
return mTileDataSource.getAttribution();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox getBounds() {
|
||||||
|
return mTileDataSource.getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ITileDataSource getDataSource() {
|
||||||
|
return mTileDataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return mTileDataSource.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the image format (jpg, png)
|
||||||
|
*/
|
||||||
|
public String getFormat() {
|
||||||
|
return mTileDataSource.getFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxZoom() {
|
||||||
|
return mTileDataSource.getMaxZoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinZoom() {
|
||||||
|
return mTileDataSource.getMinZoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return mTileDataSource.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return mTileDataSource.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenResult open() {
|
||||||
|
return OpenResult.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 Hannes Janetzek
|
* Copyright 2013 Hannes Janetzek
|
||||||
* Copyright 2016-2018 devemux86
|
* Copyright 2016-2019 devemux86
|
||||||
* Copyright 2018 boldtrn
|
* Copyright 2018 boldtrn
|
||||||
*
|
*
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||||
@@ -25,7 +25,7 @@ import org.oscim.tiling.source.UrlTileSource;
|
|||||||
|
|
||||||
public class OpenMapTilesMvtTileSource extends UrlTileSource {
|
public class OpenMapTilesMvtTileSource extends UrlTileSource {
|
||||||
|
|
||||||
private static final String DEFAULT_URL = "https://maps.tilehosting.com/data/v3";
|
private static final String DEFAULT_URL = "https://api.maptiler.com/tiles/v3";
|
||||||
private static final String DEFAULT_PATH = "/{Z}/{X}/{Y}.pbf";
|
private static final String DEFAULT_PATH = "/{Z}/{X}/{Y}.pbf";
|
||||||
|
|
||||||
public static class Builder<T extends Builder<T>> extends UrlTileSource.Builder<T> {
|
public static class Builder<T extends Builder<T>> extends UrlTileSource.Builder<T> {
|
||||||
|
|||||||
@@ -7,17 +7,36 @@ import org.oscim.utils.geom.GeometryUtils;
|
|||||||
public class GeometryTest {
|
public class GeometryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsTrisClockwise() {
|
public void testClosestPointOnLine2D() {
|
||||||
// Coordinate system is LHS
|
float[] pP = {4, 1};
|
||||||
float[] pA = new float[]{0, 0};
|
float[] pL = {1, 2};
|
||||||
float[] pB = new float[]{1, 0};
|
float[] vL = {1, -1};
|
||||||
float[] pC = new float[]{1, 1};
|
|
||||||
|
|
||||||
float area = GeometryUtils.isTrisClockwise(pA, pB, pC);
|
float[] point = GeometryUtils.closestPointOnLine2D(pP, pL, vL);
|
||||||
Assert.assertTrue(area > 0);
|
Assert.assertEquals(point[0], 3, 0.00001);
|
||||||
|
Assert.assertEquals(point[1], 0, 0.00001);
|
||||||
|
}
|
||||||
|
|
||||||
area = GeometryUtils.isTrisClockwise(pA, pC, pB);
|
@Test
|
||||||
Assert.assertTrue(area < 0);
|
public void testDistancePointLine2D() {
|
||||||
|
float[] pP = {1, 0};
|
||||||
|
float[] pL = {0, 0};
|
||||||
|
float[] vL = {2, 2};
|
||||||
|
|
||||||
|
float distance = GeometryUtils.distancePointLine2D(pP, pL, vL);
|
||||||
|
Assert.assertEquals(distance, Math.sqrt(2) / 2, 0.00001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDotProduct() {
|
||||||
|
float[] p = {-1, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
p[4] = (float) Math.cos(Math.toRadians(i * 45));
|
||||||
|
p[5] = (float) Math.sin(Math.toRadians(i * 45));
|
||||||
|
System.out.println("\n> " + (i * 45) + " " + p[3] + ":" + p[4] + "\n="
|
||||||
|
+ GeometryUtils.dotProduct(p, 0, 2, 4));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -34,14 +53,16 @@ public class GeometryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDotProduct() {
|
public void testIsTrisClockwise() {
|
||||||
float[] p = {-1, 0, 0, 0, 0, 0};
|
// Coordinate system is LHS
|
||||||
|
float[] pA = new float[]{0, 0};
|
||||||
|
float[] pB = new float[]{1, 0};
|
||||||
|
float[] pC = new float[]{1, 1};
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++) {
|
float area = GeometryUtils.isTrisClockwise(pA, pB, pC);
|
||||||
p[4] = (float) Math.cos(Math.toRadians(i * 45));
|
Assert.assertTrue(area > 0);
|
||||||
p[5] = (float) Math.sin(Math.toRadians(i * 45));
|
|
||||||
System.out.println("\n> " + (i * 45) + " " + p[3] + ":" + p[4] + "\n="
|
area = GeometryUtils.isTrisClockwise(pA, pC, pB);
|
||||||
+ GeometryUtils.dotProduct(p, 0, 2, 4));
|
Assert.assertTrue(area < 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||||
* Copyright 2012 Hannes Janetzek
|
* Copyright 2012 Hannes Janetzek
|
||||||
* Copyright 2014 Ludwig M Brinckmann
|
* Copyright 2014 Ludwig M Brinckmann
|
||||||
* Copyright 2016-2017 devemux86
|
* Copyright 2016-2019 devemux86
|
||||||
|
* Copyright 2019 Andrea Antonello
|
||||||
*
|
*
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||||
*
|
*
|
||||||
@@ -577,6 +578,17 @@ public final class MercatorProjection {
|
|||||||
return pixelYToLatitude(tileY * Tile.SIZE, getMapSize(zoomLevel));
|
return pixelYToLatitude(tileY * Tile.SIZE, getMapSize(zoomLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a tile Y number at a certain zoom level to TMS notation.
|
||||||
|
*
|
||||||
|
* @param tileY the tile Y number that should be converted.
|
||||||
|
* @param zoomLevel the zoom level at which the number should be converted.
|
||||||
|
* @return the TMS value of the tile Y number.
|
||||||
|
*/
|
||||||
|
public static long tileYToTMS(long tileY, byte zoomLevel) {
|
||||||
|
return (long) (zoomLevelToScale(zoomLevel) - tileY - 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static double toLatitude(double y) {
|
public static double toLatitude(double y) {
|
||||||
return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI;
|
return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,7 @@ import org.oscim.utils.math.MathUtils;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides utils for S3DB layers.
|
* Provides utils for S3DB layers.
|
||||||
@@ -413,7 +407,7 @@ public final class S3DBUtils {
|
|||||||
|
|
||||||
int countConcavAngles = 0;
|
int countConcavAngles = 0;
|
||||||
for (Byte simpleAngle : simpleAngles) {
|
for (Byte simpleAngle : simpleAngles) {
|
||||||
if (simpleAngle > 1)
|
if (simpleAngle < -1)
|
||||||
countConcavAngles++;
|
countConcavAngles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,14 +441,14 @@ public final class S3DBUtils {
|
|||||||
byte direction = simpleAngles.get(shift);
|
byte direction = simpleAngles.get(shift);
|
||||||
if (direction == 0) {
|
if (direction == 0) {
|
||||||
continue; // direction is similar to last one
|
continue; // direction is similar to last one
|
||||||
} else if (direction > 0) {
|
} else if (direction < 0) {
|
||||||
// If shape turns right
|
// If shape turns left (concave)
|
||||||
float[] positionRidgeA = null;
|
float[] positionRidgeA = null;
|
||||||
float[] positionRidgeB = null;
|
float[] positionRidgeB = null;
|
||||||
|
|
||||||
// Check two previous corners
|
// Check two previous corners
|
||||||
Integer indexPrevious = getIndexPreviousLeftTurn(shift, simpleAngles);
|
Integer indexPrevious = getIndexPreviousConvexTurn(shift, simpleAngles);
|
||||||
Integer indexPrevious2 = getIndexPreviousLeftTurn(indexPrevious == null ? shift - 1 : indexPrevious, simpleAngles);
|
Integer indexPrevious2 = getIndexPreviousConvexTurn(indexPrevious == null ? shift - 1 : indexPrevious, simpleAngles);
|
||||||
|
|
||||||
if (indexPrevious != null && indexPrevious2 != null) {
|
if (indexPrevious != null && indexPrevious2 != null) {
|
||||||
// Write two previous
|
// Write two previous
|
||||||
@@ -477,8 +471,8 @@ public final class S3DBUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check two next corners
|
// Check two next corners
|
||||||
Integer indexNext = getIndexNextLeftTurn(shift, simpleAngles);
|
Integer indexNext = getIndexNextConvexTurn(shift, simpleAngles);
|
||||||
Integer indexNext2 = getIndexNextLeftTurn(indexNext == null ? shift + 1 : indexNext, simpleAngles);
|
Integer indexNext2 = getIndexNextConvexTurn(indexNext == null ? shift + 1 : indexNext, simpleAngles);
|
||||||
|
|
||||||
if (indexNext != null && indexNext2 != null) {
|
if (indexNext != null && indexNext2 != null) {
|
||||||
if (ridgePoints.get(indexNext) == null) {
|
if (ridgePoints.get(indexNext) == null) {
|
||||||
@@ -535,7 +529,7 @@ public final class S3DBUtils {
|
|||||||
|
|
||||||
// Set opposite ridge, if only one concave corner
|
// Set opposite ridge, if only one concave corner
|
||||||
if (countConcavAngles == 1) {
|
if (countConcavAngles == 1) {
|
||||||
Integer opposite = getIndexNextLeftTurn(indexNext2, simpleAngles);
|
Integer opposite = getIndexNextConvexTurn(indexNext2, simpleAngles);
|
||||||
if (opposite != null) {
|
if (opposite != null) {
|
||||||
if (isGabled)
|
if (isGabled)
|
||||||
gablePoints.remove(opposite);
|
gablePoints.remove(opposite);
|
||||||
@@ -548,12 +542,12 @@ public final class S3DBUtils {
|
|||||||
isOdd = false;
|
isOdd = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Regular right angle (left turn)
|
// Regular right angle (convex turn)
|
||||||
if (isOdd) {
|
if (isOdd) {
|
||||||
isOdd = false;
|
isOdd = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (simpleAngles.get(shift) < -1) {
|
if (simpleAngles.get(shift) > 1) {
|
||||||
isOdd = true;
|
isOdd = true;
|
||||||
}
|
}
|
||||||
if (ridgePoints.containsKey(shift) && ridgeLines.containsKey(shift)) {
|
if (ridgePoints.containsKey(shift) && ridgeLines.containsKey(shift)) {
|
||||||
@@ -563,7 +557,7 @@ public final class S3DBUtils {
|
|||||||
if (currentRidgeInd != null) {
|
if (currentRidgeInd != null) {
|
||||||
float[] intersection;
|
float[] intersection;
|
||||||
// If is gabled, then use the normal line as intersection instead of bisection, but if the angle is not right, this is usually not a gable point
|
// If is gabled, then use the normal line as intersection instead of bisection, but if the angle is not right, this is usually not a gable point
|
||||||
if (isGabled && direction < -1) {
|
if (isGabled && direction > 1) {
|
||||||
if (ridgePoints.get(currentRidgeInd) == null) {
|
if (ridgePoints.get(currentRidgeInd) == null) {
|
||||||
log.debug("Gabled intersection calc failed");
|
log.debug("Gabled intersection calc failed");
|
||||||
currentRidgeInd = null;
|
currentRidgeInd = null;
|
||||||
@@ -588,7 +582,7 @@ public final class S3DBUtils {
|
|||||||
currentRidgeInd = shift;
|
currentRidgeInd = shift;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Integer indexNext = getIndexNextLeftTurn(shift, simpleAngles);
|
Integer indexNext = getIndexNextConvexTurn(shift, simpleAngles);
|
||||||
if (indexNext == null) continue;
|
if (indexNext == null) continue;
|
||||||
if (!ridgeLines.containsKey(shift)) {
|
if (!ridgeLines.containsKey(shift)) {
|
||||||
ridgeLines.put(shift, normVectors.get(indexNext));
|
ridgeLines.put(shift, normVectors.get(indexNext));
|
||||||
@@ -620,7 +614,7 @@ public final class S3DBUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only remove ridgePoint at concave corners
|
// Only remove ridgePoint at concave corners
|
||||||
if (!isGabled || simpleAngles.get(key) > 0) {
|
if (!isGabled || simpleAngles.get(key) < 0) {
|
||||||
boolean isIn = GeometryUtils.pointInPoly(ridgeEntry.getValue()[0], ridgeEntry.getValue()[1], points, points.length, 0);
|
boolean isIn = GeometryUtils.pointInPoly(ridgeEntry.getValue()[0], ridgeEntry.getValue()[1], points, points.length, 0);
|
||||||
if (!isIn) {
|
if (!isIn) {
|
||||||
// FIXME can improve shapes with concaves that intersect each other and remove shapes which have ridgepoints outside the outline
|
// FIXME can improve shapes with concaves that intersect each other and remove shapes which have ridgepoints outside the outline
|
||||||
@@ -827,7 +821,7 @@ public final class S3DBUtils {
|
|||||||
|
|
||||||
int indexStart = getIndicesLongestSide(simpleAngles, lengths, null)[0];
|
int indexStart = getIndicesLongestSide(simpleAngles, lengths, null)[0];
|
||||||
if (orientationAcross) {
|
if (orientationAcross) {
|
||||||
Integer tmp = getIndexPreviousLeftTurn(indexStart, simpleAngles);
|
Integer tmp = getIndexPreviousConvexTurn(indexStart, simpleAngles);
|
||||||
if (tmp == null) {
|
if (tmp == null) {
|
||||||
tmp = getIndexNextTurn(indexStart, simpleAngles);
|
tmp = getIndexNextTurn(indexStart, simpleAngles);
|
||||||
}
|
}
|
||||||
@@ -1081,14 +1075,14 @@ public final class S3DBUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the index of next left turn after specified index
|
* @return the index of convex turn after specified index or null, if it's concave.
|
||||||
*/
|
*/
|
||||||
private static Integer getIndexNextLeftTurn(int index, List<Byte> simpleAngles) {
|
private static Integer getIndexNextConvexTurn(int index, List<Byte> simpleAngles) {
|
||||||
for (int i = index + 1; i < simpleAngles.size() + index; i++) {
|
for (int i = index + 1; i < simpleAngles.size() + index; i++) {
|
||||||
int iMod = i % simpleAngles.size();
|
int iMod = i % simpleAngles.size();
|
||||||
if (simpleAngles.get(iMod) < 0) {
|
if (simpleAngles.get(iMod) > 0) {
|
||||||
return iMod;
|
return iMod;
|
||||||
} else if (simpleAngles.get(iMod) > 0) {
|
} else if (simpleAngles.get(iMod) < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1109,14 +1103,14 @@ public final class S3DBUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the index of previous left turn at specified index
|
* @return the index of previous convex turn at specified index
|
||||||
*/
|
*/
|
||||||
private static Integer getIndexPreviousLeftTurn(int index, List<Byte> simpleAngles) {
|
private static Integer getIndexPreviousConvexTurn(int index, List<Byte> simpleAngles) {
|
||||||
for (int i = simpleAngles.size() + index - 1; i >= 0; i--) {
|
for (int i = simpleAngles.size() + index - 1; i >= 0; i--) {
|
||||||
int iMod = i % simpleAngles.size();
|
int iMod = i % simpleAngles.size();
|
||||||
if (simpleAngles.get(iMod) < 0) {
|
if (simpleAngles.get(iMod) > 0) {
|
||||||
return iMod;
|
return iMod;
|
||||||
} else if (simpleAngles.get(iMod) > 0) {
|
} else if (simpleAngles.get(iMod) < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1132,10 +1126,10 @@ public final class S3DBUtils {
|
|||||||
Integer concaveStart = null;
|
Integer concaveStart = null;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (indexStart != null && concaveStart != null) break;
|
if (indexStart != null && concaveStart != null) break;
|
||||||
if (indexStart == null && simpleAngles.get(i) < -1) {
|
if (indexStart == null && simpleAngles.get(i) > 1) {
|
||||||
// Use first angle as start index;
|
// Use first angle as start index;
|
||||||
indexStart = i;
|
indexStart = i;
|
||||||
} else if (concaveStart == null && simpleAngles.get(i) > 1) {
|
} else if (concaveStart == null && simpleAngles.get(i) < -1) {
|
||||||
// A real concave corner
|
// A real concave corner
|
||||||
concaveStart = i;
|
concaveStart = i;
|
||||||
}
|
}
|
||||||
@@ -1156,9 +1150,9 @@ public final class S3DBUtils {
|
|||||||
|
|
||||||
// Calculate longest side with right angle next to it.
|
// Calculate longest side with right angle next to it.
|
||||||
int[] iLongSide = getIndicesLongestSide(simpleAngles, lengths, indexStart);
|
int[] iLongSide = getIndicesLongestSide(simpleAngles, lengths, indexStart);
|
||||||
if (simpleAngles.get(iLongSide[1]) > -2) {
|
if (simpleAngles.get(iLongSide[1]) < 2) {
|
||||||
// If angle is not good to start a ridge use previous
|
// If angle is not good to start a ridge use previous
|
||||||
indexStart = getIndexPreviousLeftTurn(iLongSide[0], simpleAngles);
|
indexStart = getIndexPreviousConvexTurn(iLongSide[0], simpleAngles);
|
||||||
} else {
|
} else {
|
||||||
indexStart = iLongSide[1]; // Get side next to longest one
|
indexStart = iLongSide[1]; // Get side next to longest one
|
||||||
}
|
}
|
||||||
@@ -1180,8 +1174,8 @@ public final class S3DBUtils {
|
|||||||
int size = simpleAngles.size();
|
int size = simpleAngles.size();
|
||||||
if (indexStart == null) {
|
if (indexStart == null) {
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (simpleAngles.get(i) < 0) {
|
if (simpleAngles.get(i) > 0) {
|
||||||
// Use first angle as start index;
|
// Use first convex angle as start index;
|
||||||
indexStart = i;
|
indexStart = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1302,8 +1296,12 @@ public final class S3DBUtils {
|
|||||||
* @param normVectors the normalized vectors
|
* @param normVectors the normalized vectors
|
||||||
* @return a list of simple angles:
|
* @return a list of simple angles:
|
||||||
* 0 straight
|
* 0 straight
|
||||||
* (+/-) 1 (right/left) obtuse angle
|
* (+/-) 1 (convex/concave) obtuse angle
|
||||||
* (+/-) 2 (right/left) right angle (or acute angle)
|
* (+/-) 2 (convex/concave) right angle (or acute angle)
|
||||||
|
* <p>
|
||||||
|
* Note lhs coordinate system.
|
||||||
|
* convex: turns right
|
||||||
|
* concave: turns left
|
||||||
*/
|
*/
|
||||||
private static List<Byte> getSimpleAngles(List<float[]> normVectors) {
|
private static List<Byte> getSimpleAngles(List<float[]> normVectors) {
|
||||||
int size = normVectors.size();
|
int size = normVectors.size();
|
||||||
@@ -1319,8 +1317,8 @@ public final class S3DBUtils {
|
|||||||
float angle = (float) Math.acos(Math.abs(val) > 1 ? Math.signum(val) : val);
|
float angle = (float) Math.acos(Math.abs(val) > 1 ? Math.signum(val) : val);
|
||||||
// angles.add(angle);
|
// angles.add(angle);
|
||||||
|
|
||||||
// Positive is turns right, negative turns left
|
// Positive turns right (convex), negative turns left (concave)
|
||||||
byte simpAngle = (byte) Math.signum(v1[0] * (-v2[1]) + v1[1] * v2[0]);
|
byte simpAngle = (byte) Math.signum(v1[0] * v2[1] - v1[1] * v2[0]);
|
||||||
if (angle > (MathUtils.PI / 2) - threshold) {
|
if (angle > (MathUtils.PI / 2) - threshold) {
|
||||||
// Right angle
|
// Right angle
|
||||||
simpAngle *= 2;
|
simpAngle *= 2;
|
||||||
|
|||||||
@@ -131,13 +131,9 @@ public final class GeometryUtils {
|
|||||||
if (out == null)
|
if (out == null)
|
||||||
out = new float[2];
|
out = new float[2];
|
||||||
|
|
||||||
// Calculate center
|
|
||||||
for (int i = 0; i < n; i += 2, pointPos += 2) {
|
for (int i = 0; i < n; i += 2, pointPos += 2) {
|
||||||
float x = points[pointPos];
|
out[0] += points[pointPos];
|
||||||
float y = points[pointPos + 1];
|
out[1] += points[pointPos + 1];
|
||||||
|
|
||||||
out[0] += x;
|
|
||||||
out[1] += y;
|
|
||||||
}
|
}
|
||||||
out[0] = out[0] * 2 / n;
|
out[0] = out[0] * 2 / n;
|
||||||
out[1] = out[1] * 2 / n;
|
out[1] = out[1] * 2 / n;
|
||||||
@@ -145,6 +141,21 @@ public final class GeometryUtils {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the closest point on a line.
|
||||||
|
* See: https://en.wikipedia.org/wiki/Vector_projection#Vector_projection_2
|
||||||
|
*
|
||||||
|
* @param pP point
|
||||||
|
* @param pL point of line
|
||||||
|
* @param vL vector of line
|
||||||
|
* @return the closest point on line
|
||||||
|
*/
|
||||||
|
public static float[] closestPointOnLine2D(float[] pP, float[] pL, float[] vL) {
|
||||||
|
float[] vPL = diffVec(pL, pP);
|
||||||
|
float[] vPS = diffVec(vPL, scale(vL, dotProduct(vPL, vL) / dotProduct(vL, vL)));
|
||||||
|
return sumVec(pP, vPS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param a first vector
|
* @param a first vector
|
||||||
* @param b second vector
|
* @param b second vector
|
||||||
@@ -222,6 +233,9 @@ public final class GeometryUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Calculate the distance from a point to a line.
|
||||||
|
* See: https://en.wikipedia.org/wiki/Vector_projection#Vector_projection_2
|
||||||
|
*
|
||||||
* @param pP point
|
* @param pP point
|
||||||
* @param pL point of line
|
* @param pL point of line
|
||||||
* @param vL vector of line
|
* @param vL vector of line
|
||||||
@@ -229,7 +243,7 @@ public final class GeometryUtils {
|
|||||||
*/
|
*/
|
||||||
public static float distancePointLine2D(float[] pP, float[] pL, float[] vL) {
|
public static float distancePointLine2D(float[] pP, float[] pL, float[] vL) {
|
||||||
float[] vPL = diffVec(pL, pP);
|
float[] vPL = diffVec(pL, pP);
|
||||||
float[] vPS = diffVec(vPL, scale(vL, dotProduct(vPL, vL)));
|
float[] vPS = diffVec(vPL, scale(vL, dotProduct(vPL, vL) / dotProduct(vL, vL)));
|
||||||
return (float) Math.sqrt(dotProduct(vPS, vPS));
|
return (float) Math.sqrt(dotProduct(vPS, vPS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user