diff --git a/settings.gradle b/settings.gradle index 93f56fc4..0c12edc5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,3 +9,6 @@ include ':vtm-gdx-desktop' include ':vtm-gdx-android' include ':vtm-gdx-html' include ':vtm-gdx-ios' +include ':vtm-jeo' +include ':vtm-jeo-desktop' +include ':vtm-jeo-android' diff --git a/vtm-jeo-android/AndroidManifest.xml b/vtm-jeo-android/AndroidManifest.xml new file mode 100644 index 00000000..7dff7e16 --- /dev/null +++ b/vtm-jeo-android/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vtm-jeo-android/build.gradle b/vtm-jeo-android/build.gradle new file mode 100644 index 00000000..0cac9e9f --- /dev/null +++ b/vtm-jeo-android/build.gradle @@ -0,0 +1,91 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.8.+' + } +} + +apply plugin: 'android' + +dependencies { + compile project(':vtm-jeo') + compile 'org.oscim:vtm-android:0.5.9-SNAPSHOT' + compile 'org.oscim:vtm-themes:0.5.9-SNAPSHOT' +} + +android { + compileSdkVersion 19 + buildToolsVersion '19.0.1' + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + resources.srcDirs = ['src', 'assets'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + } + + debug.setRoot('build-types/debug') + release.setRoot('build-types/release') + } + + packagingOptions { + //exclude 'META-INF/**/*' + exclude 'META-INF/services/org.jeo.data.Driver' + } + // ignore deprecated + lintOptions.abortOnError false +} + +// Including configurations into Eclipse +eclipse { + sourceCompatibility = 1.6 + targetCompatibility = 1.6 + + // Configuring Eclipse classpath + classpath { + plusConfigurations += configurations.compile + + defaultOutputDir = file('bin/classes') + + file { + // Direct manipulation of the generated classpath XML + withXml { + def node = it.asNode() + node.appendNode('classpathentry kind="src" path="src"') + node.appendNode('classpathentry kind="src" path="gen"') + } + + whenMerged { classpath -> + classpath.entries.removeAll { entry -> + entry.path.contains('vtm-android-0.5.9') } + classpath.entries.removeAll { entry -> + entry.path.contains('vtm-0.5.9') } + } + } + } + + project { + natures = ['com.android.ide.eclipse.adt.AndroidNature', + 'org.eclipse.jdt.core.javanature'] + + buildCommand 'com.android.ide.eclipse.adt.ResourceManagerBuilder' + buildCommand 'com.android.ide.eclipse.adt.PreCompilerBuilder' + buildCommand 'com.android.ide.eclipse.adt.ApkBuilder' + } +} + +task run (dependsOn: 'installDebug'){ + doFirst { + println(">> adb run...") + String adb = System.getenv()['ANDROID_HOME'] + '/platform-tools/adb' + String cmd = "${adb} shell am start -n org.oscim.jeo.android/.TestActivity" + def proc = cmd.execute() + proc.in.eachLine {line -> println line} + proc.err.eachLine {line -> System.err.println( 'ERROR: ' + line)} + proc.waitFor() + } +} \ No newline at end of file diff --git a/vtm-jeo-android/project.properties b/vtm-jeo-android/project.properties new file mode 100644 index 00000000..1a75492e --- /dev/null +++ b/vtm-jeo-android/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 +android.library.reference.1=../../vtm-android diff --git a/vtm-jeo-android/res/drawable/ic_launcher.png b/vtm-jeo-android/res/drawable/ic_launcher.png new file mode 100644 index 00000000..6496c5ad Binary files /dev/null and b/vtm-jeo-android/res/drawable/ic_launcher.png differ diff --git a/vtm-jeo-android/res/layout/activity_map.xml b/vtm-jeo-android/res/layout/activity_map.xml new file mode 100644 index 00000000..e761d147 --- /dev/null +++ b/vtm-jeo-android/res/layout/activity_map.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vtm-jeo-android/src/org/oscim/jeo/android/TestActivity.java b/vtm-jeo-android/src/org/oscim/jeo/android/TestActivity.java new file mode 100644 index 00000000..5a40cd79 --- /dev/null +++ b/vtm-jeo-android/src/org/oscim/jeo/android/TestActivity.java @@ -0,0 +1,159 @@ +package org.oscim.jeo.android; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +import org.jeo.data.VectorDataset; +import org.jeo.map.Style; +import org.oscim.android.MapActivity; +import org.oscim.layers.OSMIndoorLayer; +import org.oscim.layers.tile.vector.BuildingLayer; +import org.oscim.layers.tile.vector.VectorTileLayer; +import org.oscim.layers.tile.vector.labeling.LabelLayer; +import org.oscim.renderer.MapRenderer; +import org.oscim.test.JeoTest; +import org.oscim.theme.VtmThemes; +import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; +import org.oscim.utils.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.widget.Toast; +import android.widget.ToggleButton; + +public class TestActivity extends MapActivity { + public static final Logger log = LoggerFactory.getLogger(TestActivity.class); + + // from http://overpass-turbo.eu/s/2vp + String PATH = "https://gist.github.com/anonymous/8960337/raw/overpass.geojson"; + //String PATH = "https://gist.github.com/hjanetzek/9280925/raw/overpass.geojson"; + + private OSMIndoorLayer mIndoorLayer; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map); + + MapRenderer.setBackgroundColor(0xff909090); + + mMap.addTask(new Runnable() { + @Override + public void run() { + showToast("load data"); + InputStream is = null; + try { + //File file = new File(Environment.getExternalStorageDirectory() + // .getAbsolutePath(), "osmindoor.json"); + //is = new FileInputStream(file); + + URL url = new URL(PATH); + URLConnection conn = url.openConnection(); + is = conn.getInputStream(); + loadJson(is); + } catch (IOException e) { + e.printStackTrace(); + } finally { + IOUtils.closeQuietly(is); + } + } + }); + + VectorTileLayer baseLayer = mMap.setBaseMap(new OSciMap4TileSource()); + mMap.layers().add(new BuildingLayer(mMap, baseLayer)); + mMap.layers().add(new LabelLayer(mMap, baseLayer)); + mMap.setTheme(VtmThemes.TRON2); + + //mMap.setMapPosition(49.417, 8.673, 1 << 17); + mMap.setMapPosition(53.5620092, 9.9866457, 1 << 16); + + // mMap.layers().add(new TileGridLayer(mMap)); + // String file = Environment.getExternalStorageDirectory().getAbsolutePath(); + // VectorDataset data = (VectorDataset) JeoTest.getJsonData(file + "/states.json", true); + // Style style = JeoTest.getStyle(); + // mMap.layers().add(new JeoVectorLayer(mMap, data, style)); + } + + void loadJson(InputStream is) { + showToast("got data"); + + VectorDataset data = JeoTest.readGeoJson(is); + Style style = JeoTest.getStyle(); + mIndoorLayer = new OSMIndoorLayer(mMap, data, style); + mMap.layers().add(mIndoorLayer); + + showToast("data ready"); + mMap.updateMap(true); + + mIndoorLayer.activeLevels[0] = true; + shift(); + } + + public void showToast(final String text) { + final Context ctx = this; + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast toast = Toast.makeText(ctx, text, Toast.LENGTH_SHORT); + toast.show(); + } + }); + } + + boolean mShift = true; + + public void shift() { + if (!mShift) + return; + + mMap.postDelayed(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < 10; i++) { + if (mIndoorLayer.activeLevels[i]) { + mIndoorLayer.activeLevels[i] = false; + mIndoorLayer.activeLevels[(i + 1) % 9] = true; + mIndoorLayer.update(); + break; + } + } + shift(); + } + }, 200); + + } + + public void onClick(View v) { + mShift = false; + + if (mIndoorLayer == null) + return; + + int i = 0; + + if (v instanceof ToggleButton) { + ToggleButton b = (ToggleButton) v; + i = (b.getTextOn().charAt(0) - '0') + 1; + } + + if (i < 0 || i > 9) + i = 0; + + mIndoorLayer.activeLevels[i] ^= true; + ((ToggleButton) v).setChecked(mIndoorLayer.activeLevels[i]); + log.debug(Arrays.toString(mIndoorLayer.activeLevels)); + mIndoorLayer.update(); + } + + @Override + protected void onStop() { + super.onStop(); + } +} diff --git a/vtm-jeo-desktop/build.gradle b/vtm-jeo-desktop/build.gradle new file mode 100644 index 00000000..dd104bcc --- /dev/null +++ b/vtm-jeo-desktop/build.gradle @@ -0,0 +1,22 @@ +repositories { + // libgdx + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } +} + +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'application' + +mainClassName = 'org.oscim.jeo.test.LayerTest' + +sourceSets { + main.java.srcDirs = ['src'] + main.resources.srcDirs = ['resources'] +} + +dependencies { + compile project(':vtm-jeo') + compile 'org.oscim:vtm-gdx-desktop:0.5.9-SNAPSHOT' +} + +run { ignoreExitValue = true } diff --git a/vtm-jeo-desktop/src/org/oscim/jeo/test/LayerTest.java b/vtm-jeo-desktop/src/org/oscim/jeo/test/LayerTest.java new file mode 100644 index 00000000..5fe5a2ed --- /dev/null +++ b/vtm-jeo-desktop/src/org/oscim/jeo/test/LayerTest.java @@ -0,0 +1,27 @@ +package org.oscim.jeo.test; + +import org.jeo.data.VectorDataset; +import org.jeo.map.Style; +import org.oscim.gdx.GdxMap; +import org.oscim.gdx.GdxMapApp; +import org.oscim.layers.JeoVectorLayer; +import org.oscim.test.JeoTest; + +public class LayerTest extends GdxMap { + + @Override + public void createLayers() { + //JeoTest.indoorSketch(mMap, "osmindoor.json"); + //mMap.setMapPosition(49.417, 8.673, 1 << 17); + + VectorDataset data = (VectorDataset) JeoTest.getJsonData("states.json", true); + Style style = JeoTest.getStyle(); + + mMap.layers().add(new JeoVectorLayer(mMap, data, style)); + } + + public static void main(String[] args) { + GdxMapApp.init(); + GdxMapApp.run(new LayerTest(), null, 256); + } +} diff --git a/vtm-jeo-desktop/src/org/oscim/jeo/test/ThemeTest.java b/vtm-jeo-desktop/src/org/oscim/jeo/test/ThemeTest.java new file mode 100644 index 00000000..57eff695 --- /dev/null +++ b/vtm-jeo-desktop/src/org/oscim/jeo/test/ThemeTest.java @@ -0,0 +1,34 @@ +package org.oscim.jeo.test; + +import org.oscim.gdx.GdxMapApp; +import org.oscim.layers.TileGridLayer; +import org.oscim.layers.tile.vector.VectorTileLayer; +import org.oscim.renderer.MapRenderer; +import org.oscim.theme.carto.RenderTheme; +import org.oscim.tiling.source.UrlTileSource; +import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; + +public class ThemeTest extends GdxMapApp { + + public static void main(String[] args) { + GdxMapApp.init(); + GdxMapApp.run(new ThemeTest(), null, 256); + } + + @Override + public void createLayers() { + UrlTileSource ts = new OSciMap4TileSource(); + + VectorTileLayer l = mMap.setBaseMap(ts); + + l.setRenderTheme(new RenderTheme()); + + MapRenderer.setBackgroundColor(0xffcccccc); + + // mMap.getLayers().add(new LabelLayer(mMap, + // mMapLayer.getTileLayer())); + // mMap.getLayers().add(new JeoMapLayer(mMap)); + + mMap.layers().add(new TileGridLayer(mMap)); + } +} diff --git a/vtm-jeo/build.gradle b/vtm-jeo/build.gradle new file mode 100644 index 00000000..5e980a98 --- /dev/null +++ b/vtm-jeo/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'java' +apply plugin: 'maven' + +repositories { + mavenCentral() + mavenLocal() +} + +sourceSets { + main.java.srcDirs = ['src'] + main.resources.srcDirs = ['src'] +} + +dependencies { + compile 'org.oscim:vtm:0.5.9-SNAPSHOT' + compile ('org.jeo:jeo:0-SNAPSHOT') { + exclude group: 'org.slf4j', module: 'slf4j-jdk14' + } + compile ('org.jeo:jeo-carto:0-SNAPSHOT') { + exclude group: 'org.slf4j', module: 'slf4j-jdk14' + } +} + +eclipse.classpath.file.whenMerged { classpath -> + classpath.entries.findAll { entry -> + entry.path.contains('vtm-0.5.9-SNAPSHOT.jar') }*.exported = false +} diff --git a/vtm-jeo/src/org/oscim/jeo/JeoUtils.java b/vtm-jeo/src/org/oscim/jeo/JeoUtils.java new file mode 100644 index 00000000..0395d589 --- /dev/null +++ b/vtm-jeo/src/org/oscim/jeo/JeoUtils.java @@ -0,0 +1,12 @@ +package org.oscim.jeo; + +import org.jeo.map.RGB; + +public class JeoUtils { + public static int color(RGB rgb) { + return rgb.getAlpha() << 24 + | rgb.getRed() << 16 + | rgb.getGreen() << 8 + | rgb.getBlue(); + } +} diff --git a/vtm-jeo/src/org/oscim/layers/JeoTileLayer.java b/vtm-jeo/src/org/oscim/layers/JeoTileLayer.java new file mode 100644 index 00000000..f9162dc9 --- /dev/null +++ b/vtm-jeo/src/org/oscim/layers/JeoTileLayer.java @@ -0,0 +1,35 @@ +package org.oscim.layers; + +import org.oscim.layers.tile.MapTile; +import org.oscim.layers.tile.TileLoader; +import org.oscim.layers.tile.TileManager; +import org.oscim.layers.tile.bitmap.BitmapTileLayer; +import org.oscim.map.Map; +import org.oscim.tiling.source.bitmap.BitmapTileSource; + +public class JeoTileLayer extends BitmapTileLayer { + + public JeoTileLayer(Map map, BitmapTileSource tileSource) { + super(map, tileSource); + } + + @Override + protected TileLoader createLoader(TileManager tm) { + return new TileLoader(tm) { + + @Override + public void cleanup() { + // TODO Auto-generated method stub + + } + + @Override + protected boolean executeJob(MapTile tile) { + // TODO Auto-generated method stub + return false; + } + + }; + } + +} diff --git a/vtm-jeo/src/org/oscim/layers/JeoTileSource.java b/vtm-jeo/src/org/oscim/layers/JeoTileSource.java new file mode 100644 index 00000000..0648e10c --- /dev/null +++ b/vtm-jeo/src/org/oscim/layers/JeoTileSource.java @@ -0,0 +1,80 @@ +package org.oscim.layers; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import org.jeo.data.Tile; +import org.jeo.data.TileDataset; +import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.canvas.Bitmap; +import org.oscim.layers.tile.MapTile; +import org.oscim.tiling.ITileDataSink; +import org.oscim.tiling.ITileDataSource; +import org.oscim.tiling.TileSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JeoTileSource extends TileSource { + final static Logger log = LoggerFactory.getLogger(JeoTileSource.class); + + final TileDataset mTileDataset; + + public JeoTileSource(TileDataset tileDataset) { + log.debug("load tileset {}", tileDataset.getName()); + mTileDataset = tileDataset; + //mTileDataset.pyramid(). + mZoomMax = 1; + mZoomMin = 0; + } + + @Override + public ITileDataSource getDataSource() { + return new ITileDataSource() { + + @Override + public QueryResult executeQuery(MapTile tile, ITileDataSink sink) { + log.debug("query {}", tile); + try { + Tile t = mTileDataset.read(tile.zoomLevel, tile.tileX, + // flip Y axis + (1 << tile.zoomLevel) - 1 - tile.tileY); + if (t == null) { + log.debug("not found {}", tile); + return QueryResult.TILE_NOT_FOUND; + } + Bitmap b = CanvasAdapter.g.decodeBitmap(new ByteArrayInputStream(t.getData())); + sink.setTileImage(b); + log.debug("success {}", tile); + + return QueryResult.SUCCESS; + + } catch (IOException e) { + e.printStackTrace(); + } + log.debug("fail {}", tile); + + return QueryResult.FAILED; + } + + @Override + public void destroy() { + + } + }; + } + + int mRefs; + + @Override + public OpenResult open() { + mRefs++; + return OpenResult.SUCCESS; + } + + @Override + public void close() { + if (--mRefs == 0) + mTileDataset.close(); + } + +} diff --git a/vtm-jeo/src/org/oscim/layers/JeoVectorLayer.java b/vtm-jeo/src/org/oscim/layers/JeoVectorLayer.java new file mode 100644 index 00000000..8f47e9ec --- /dev/null +++ b/vtm-jeo/src/org/oscim/layers/JeoVectorLayer.java @@ -0,0 +1,150 @@ +package org.oscim.layers; + +import java.io.IOException; + +import org.jeo.data.Query; +import org.jeo.data.VectorDataset; +import org.jeo.feature.Feature; +import org.jeo.geom.Geom; +import org.jeo.map.CartoCSS; +import org.jeo.map.RGB; +import org.jeo.map.Rule; +import org.jeo.map.RuleList; +import org.jeo.map.Style; +import org.oscim.jeo.JeoUtils; +import org.oscim.map.Map; +import org.oscim.renderer.elements.LineLayer; +import org.oscim.renderer.elements.MeshLayer; +import org.oscim.theme.styles.Area; +import org.oscim.theme.styles.Line; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.LineString; + +public class JeoVectorLayer extends JtsLayer { + + public static final Logger log = LoggerFactory.getLogger(JeoVectorLayer.class); + static final boolean dbg = false; + + private final VectorDataset mDataset; + private final RuleList mRules; + + protected double mDropPointDistance = 0.01; + + public JeoVectorLayer(Map map, VectorDataset data, Style style) { + super(map); + mDataset = data; + + mRules = style.getRules().selectById(data.getName(), true).flatten(); + //mRules = style.getRules().selectById("way", true).flatten(); + log.debug(mRules.toString()); + + mRenderer = new Renderer(); + } + + @Override + protected void processFeatures(Task t, Envelope b) { + if (mDropPointDistance > 0) { + /* reduce lines points min distance */ + mMinX = ((b.getMaxX() - b.getMinX()) / mMap.getWidth()); + mMinY = ((b.getMaxY() - b.getMinY()) / mMap.getHeight()); + mMinX *= mDropPointDistance; + mMinY *= mDropPointDistance; + } + + try { + Query q = new Query().bounds(b); + if (dbg) + log.debug("query {}", b); + for (Feature f : mDataset.cursor(q)) { + if (dbg) + log.debug("feature {}", f); + + RuleList rs = mRules.match(f); + if (rs.isEmpty()) + continue; + + Rule r = rs.collapse(); + if (r == null) + continue; + + Geometry g = f.geometry(); + if (g == null) + continue; + + switch (Geom.Type.from(g)) { + case POINT: + addPoint(t, f, r, g); + break; + case MULTIPOINT: + for (int i = 0, n = g.getNumGeometries(); i < n; i++) + addPoint(t, f, r, g.getGeometryN(i)); + break; + case LINESTRING: + addLine(t, f, r, g); + break; + case MULTILINESTRING: + for (int i = 0, n = g.getNumGeometries(); i < n; i++) + addLine(t, f, r, g.getGeometryN(i)); + break; + case POLYGON: + addPolygon(t, f, r, g); + break; + case MULTIPOLYGON: + for (int i = 0, n = g.getNumGeometries(); i < n; i++) + addPolygon(t, f, r, g.getGeometryN(i)); + break; + default: + break; + } + } + } catch (IOException e) { + log.error("Error querying layer " + mDataset.getName() + e); + } + } + + protected void addLine(Task t, Feature f, Rule rule, Geometry g) { + + if (((LineString) g).isClosed()) { + addPolygon(t, f, rule, g); + return; + } + + LineLayer ll = t.layers.getLineLayer(2); + if (ll.line == null) { + RGB color = rule.color(f, CartoCSS.LINE_COLOR, RGB.black); + float width = rule.number(f, CartoCSS.LINE_WIDTH, 1.2f); + ll.line = new Line(0, JeoUtils.color(color), width); + ll.setDropDistance(0.5f); + } + + addLine(t, g, ll); + } + + protected void addPolygon(Task t, Feature f, Rule rule, Geometry g) { + + LineLayer ll = t.layers.getLineLayer(1); + + if (ll.line == null) { + float width = rule.number(f, CartoCSS.LINE_WIDTH, 1.2f); + RGB color = rule.color(f, CartoCSS.LINE_COLOR, RGB.black); + ll.line = new Line(0, JeoUtils.color(color), width); + ll.setDropDistance(0.5f); + } + + MeshLayer mesh = t.layers.getMeshLayer(0); + if (mesh.area == null) { + int color = JeoUtils.color(rule.color(f, CartoCSS.POLYGON_FILL, RGB.red)); + mesh.area = new Area(color); + } + + addPolygon(t, g, mesh, ll); + } + + protected void addPoint(Task t, Feature f, Rule rule, Geometry g) { + + } +} diff --git a/vtm-jeo/src/org/oscim/layers/JtsLayer.java b/vtm-jeo/src/org/oscim/layers/JtsLayer.java new file mode 100644 index 00000000..6a1cf5e3 --- /dev/null +++ b/vtm-jeo/src/org/oscim/layers/JtsLayer.java @@ -0,0 +1,102 @@ +package org.oscim.layers; + +import org.jeo.geom.CoordinatePath; +import org.oscim.core.BoundingBox; +import org.oscim.core.GeometryBuffer; +import org.oscim.core.MapPosition; +import org.oscim.core.MercatorProjection; +import org.oscim.core.Tile; +import org.oscim.layers.vector.AbstractVectorLayer; +import org.oscim.map.Map; +import org.oscim.renderer.elements.LineLayer; +import org.oscim.renderer.elements.MeshLayer; +import org.oscim.utils.geom.SimplifyDP; +import org.oscim.utils.geom.SimplifyVW; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; + +public abstract class JtsLayer extends AbstractVectorLayer { + + public JtsLayer(Map map) { + super(map); + } + + @Override + protected void processFeatures(Task t, BoundingBox bbox) { + processFeatures(t, new Envelope(bbox.getMinLongitude(), bbox.getMaxLongitude(), + bbox.getMinLatitude(), bbox.getMaxLatitude())); + + } + + protected abstract void processFeatures(Task t, Envelope e); + + protected int transformPath(MapPosition pos, GeometryBuffer g, CoordinatePath path) { + + double scale = pos.scale * Tile.SIZE / UNSCALE_COORD; + int cnt = 0; + O: while (path.hasNext()) { + Coordinate c = path.next(); + float x = (float) ((MercatorProjection.longitudeToX(c.x) - pos.x) * scale); + float y = (float) ((MercatorProjection.latitudeToY(c.y) - pos.y) * scale); + + switch (path.getStep()) { + case MOVE_TO: + if (g.isPoly()) + g.startPolygon(); + else if (g.isLine()) + g.startLine(); + + cnt++; + g.addPoint(x, y); + break; + case LINE_TO: + cnt++; + g.addPoint(x, y); + break; + case CLOSE: + //g.addPoint(x, y); + //if (g.type == GeometryType.POLY) + break; + case STOP: + break O; + } + } + return cnt; + } + + SimplifyDP mSimpDP = new SimplifyDP(); + SimplifyVW mSimpVW = new SimplifyVW(); + + protected void addPolygon(Task t, Geometry g, MeshLayer ml, LineLayer ll) { + mGeom.clear(); + mGeom.startPolygon(); + + CoordinatePath p = CoordinatePath.create(g); + if (mMinX > 0 || mMinY > 0) + p.generalize(mMinX, mMinY); + + if (transformPath(t.position, mGeom, p) < 3) + return; + + if (!mClipper.clip(mGeom)) + return; + + mSimpVW.simplify(mGeom, 0.1f); + mSimpDP.simplify(mGeom, 0.5f); + + ll.addLine(mGeom); + ml.addMesh(mGeom); + } + + protected void addLine(Task t, Geometry g, LineLayer ll) { + mGeom.clear(); + mGeom.startLine(); + + CoordinatePath p = CoordinatePath.create(g); + transformPath(t.position, mGeom, p); + + ll.addLine(mGeom); + } +} diff --git a/vtm-jeo/src/org/oscim/layers/OSMIndoorLayer.java b/vtm-jeo/src/org/oscim/layers/OSMIndoorLayer.java new file mode 100644 index 00000000..11139ea8 --- /dev/null +++ b/vtm-jeo/src/org/oscim/layers/OSMIndoorLayer.java @@ -0,0 +1,145 @@ +package org.oscim.layers; + +import java.util.HashMap; + +import org.jeo.data.VectorDataset; +import org.jeo.feature.Feature; +import org.jeo.map.CartoCSS; +import org.jeo.map.RGB; +import org.jeo.map.Rule; +import org.jeo.map.Style; +import org.oscim.backend.canvas.Color; +import org.oscim.jeo.JeoUtils; +import org.oscim.map.Map; +import org.oscim.renderer.elements.LineLayer; +import org.oscim.renderer.elements.MeshLayer; +import org.oscim.renderer.elements.TextItem; +import org.oscim.renderer.elements.TextLayer; +import org.oscim.theme.styles.Area; +import org.oscim.theme.styles.Line; +import org.oscim.theme.styles.Text; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.LineString; + +public class OSMIndoorLayer extends JeoVectorLayer { + + protected TextLayer mTextLayer; + protected Text mText = Text.createText(16, 2.2f, Color.BLACK, Color.WHITE, true); + + public OSMIndoorLayer(Map map, VectorDataset data, Style style) { + super(map, data, style); + } + + public boolean[] activeLevels = new boolean[10]; + + @Override + protected void processFeatures(Task t, Envelope b) { + mTextLayer = t.layers.addTextLayer(new TextLayer()); + + super.processFeatures(t, b); + + //render TextItems to a bitmap and prepare vertex buffer data. + mTextLayer.prepare(); + mTextLayer.clearLabels(); + } + + protected void addLine(Task t, Feature f, Rule rule, Geometry g) { + + if (((LineString) g).isClosed()) { + addPolygon(t, f, rule, g); + return; + } + + int level = getLevel(f); + + LineLayer ll = t.layers.getLineLayer(level * 3 + 2); + if (ll.line == null) { + RGB color = rule.color(f, CartoCSS.LINE_COLOR, RGB.black); + float width = rule.number(f, CartoCSS.LINE_WIDTH, 1.2f); + ll.line = new Line(0, JeoUtils.color(color), width); + ll.heightOffset = level * 4; + ll.setDropDistance(0); + } + + addLine(t, g, ll); + } + + protected void addPolygon(Task t, Feature f, Rule rule, Geometry g) { + int level = getLevel(f); + + LineLayer ll = t.layers.getLineLayer(level * 3 + 1); + + boolean active = activeLevels[level + 1]; + + if (ll.line == null) { + float width = rule.number(f, CartoCSS.LINE_WIDTH, 1.2f); + int color = Color.rainbow((level + 1) / 10f); + + if (level > -2 && !active) + color = Color.fade(color, 0.1f); + + ll.line = new Line(0, color, width); + ll.heightOffset = level * 4; + ll.setDropDistance(0); + } + + MeshLayer mesh = t.layers.getMeshLayer(level * 3); + if (mesh.area == null) { + int color = JeoUtils.color(rule.color(f, CartoCSS.POLYGON_FILL, RGB.red)); + if (level > -2 && !active) + color = Color.fade(color, 0.1f); + + mesh.area = new Area(color); + //mesh.area = new Area(Color.fade(Color.DKGRAY, 0.1f)); + mesh.heightOffset = level * 4f; + } + + addPolygon(t, g, mesh, ll); + + if (active) { + Object o = f.get("name"); + if (o instanceof String) { + float x = 0; + float y = 0; + int n = mGeom.index[0]; + for (int i = 0; i < n;) { + x += mGeom.points[i++]; + y += mGeom.points[i++]; + } + + TextItem ti = TextItem.pool.get(); + ti.set(x / (n / 2) / 8, y / (n / 2) / 8, (String) o, mText); + + mTextLayer.addText(ti); + } + } + } + + @Override + protected void addPoint(Task t, Feature f, Rule rule, Geometry g) { + + } + + private int getLevel(Feature f) { + /* not sure if one could match these geojson properties with cartocss */ + Object o = f.get("@relations"); + if (o instanceof HashMap) { + @SuppressWarnings("unchecked") + HashMap tags = (HashMap) o; + @SuppressWarnings("unchecked") + HashMap reltags = (HashMap) tags.get("reltags"); + + if (reltags != null) { + o = reltags.get("level"); + if (o instanceof String) { + //log.debug("got level {}", o); + return Integer.parseInt((String) o); + } + } + } + return 0; + } + +} diff --git a/vtm-jeo/src/org/oscim/test/JeoTest.java b/vtm-jeo/src/org/oscim/test/JeoTest.java new file mode 100644 index 00000000..d29955a3 --- /dev/null +++ b/vtm-jeo/src/org/oscim/test/JeoTest.java @@ -0,0 +1,183 @@ +package org.oscim.test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.jeo.carto.Carto; +import org.jeo.data.Dataset; +import org.jeo.data.Query; +import org.jeo.data.VectorDataset; +import org.jeo.data.mem.MemVector; +import org.jeo.data.mem.MemWorkspace; +import org.jeo.feature.Feature; +import org.jeo.feature.Features; +import org.jeo.feature.Schema; +import org.jeo.feature.SchemaBuilder; +import org.jeo.geojson.GeoJSONDataset; +import org.jeo.geojson.GeoJSONReader; +import org.jeo.geom.GeomBuilder; +import org.jeo.map.Style; +import org.oscim.layers.OSMIndoorLayer; +import org.oscim.layers.tile.vector.BuildingLayer; +import org.oscim.layers.tile.vector.VectorTileLayer; +import org.oscim.map.Map; +import org.oscim.renderer.MapRenderer; +import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; + +import com.vividsolutions.jts.geom.Geometry; + +public class JeoTest { + + public static void indoorSketch(Map map, String file) { + MapRenderer.setBackgroundColor(0xff909090); + VectorTileLayer baseLayer = map.setBaseMap(new OSciMap4TileSource()); + map.layers().add(new BuildingLayer(map, baseLayer)); + + VectorDataset data = null; + try { + data = JeoTest.readGeoJson(new FileInputStream(new File(file))); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + Style style = JeoTest.getStyle(); + map.layers().add(new OSMIndoorLayer(map, data, style)); + } + + public static Style getStyle() { + Style style = null; + + try { + style = Carto.parse("" + + "#way {" + + " line-width: 2;" + + " line-color: #c80;" + + " polygon-fill: #44111111;" + + " " + + "}" + + "#states {" + + " line-width: 2.2;" + + " line-color: #c80;" + + " polygon-fill: #44111111;" + + " " + + "}" + ); + + return style; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static VectorDataset readGeoJson(InputStream is) { + GeoJSONReader r = new GeoJSONReader(); + + @SuppressWarnings("resource") + MemWorkspace mem = new MemWorkspace(); + + //mem.put("layer", data); + try { + Schema s = new SchemaBuilder("way").schema(); + + MemVector memData = mem.create(s); + + for (Feature f : r.features(is)) { + //System.out.println("loaded: " + f); + memData.add(f); + } + return memData; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static Dataset getJsonData(String file, boolean memory) { + GeoJSONDataset data = null; + + try { + data = new GeoJSONDataset(new File(file)); + } catch (UnsupportedOperationException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + if (memory) { + @SuppressWarnings("resource") + MemWorkspace mem = new MemWorkspace(); + + //mem.put("layer", data); + try { + + Schema s = data.schema(); + Query q = new Query(); + + MemVector memData = mem.create(s); + + for (Feature f : data.cursor(q)) { + memData.add(f); + } + + //return mem.get("layer"); + return memData; + } catch (IOException e) { + e.printStackTrace(); + } + } + return data; + } + + public static Dataset getMemWorkspace(String layer) { + GeomBuilder gb = new GeomBuilder(4326); + + @SuppressWarnings("resource") + MemWorkspace mem = new MemWorkspace(); + Schema schema = new SchemaBuilder(layer) + .field("geometry", Geometry.class) + .field("id", Integer.class) + .field("name", String.class) + .field("cost", Double.class).schema(); + + MemVector data; + try { + data = mem.create(schema); + } catch (UnsupportedOperationException e) { + e.printStackTrace(); + return null; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + Geometry g = gb.point(0, 0).toPoint(); + //g.setSRID(4326); + + data.add(Features.create(null, data.schema(), + g, 1, "anvil", + 10.99)); + + data.add(Features.create(null, data.schema(), + gb.points(10, 10, 20, 20).toLineString(), + 2, "bomb", 11.99)); + + data.add(Features.create(null, data.schema(), + gb.point(100, 10).toPoint().buffer(10), + 3, "dynamite", 12.99)); + + //Dataset jsonData = new GeoJSONDataset(new File("states.json")); + //mem.put("states", jsonData); + + try { + return mem.get(layer); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } +} diff --git a/vtm-jeo/src/org/oscim/theme/carto/MatcherFeature.java b/vtm-jeo/src/org/oscim/theme/carto/MatcherFeature.java new file mode 100644 index 00000000..8e345db8 --- /dev/null +++ b/vtm-jeo/src/org/oscim/theme/carto/MatcherFeature.java @@ -0,0 +1,74 @@ +package org.oscim.theme.carto; + +import static java.lang.System.out; + +import java.util.List; +import java.util.Map; + +import org.jeo.feature.BasicFeature; +import org.oscim.core.Tag; +import org.oscim.core.TagSet; + +//imitate Feature behaviour for tags and zoom-level +class MatcherFeature extends BasicFeature { + TagSet mTags; + Integer mZoom; + + void setTags(TagSet tags) { + mTags = tags; + } + + void setZoom(int zoom) { + mZoom = Integer.valueOf(zoom); + } + + protected MatcherFeature() { + super(""); + } + + @Override + public Object get(String key) { + //out.println("get(" + key + ")"); + + if (key.equals("zoom")) + return mZoom; + + Tag t = mTags.get(key.intern()); + if (t == null) + return null; + + //out.println("value: " + t.value); + + return t.value; + } + + @Override + public void put(String key, Object val) { + out.println("EEEK put()"); + } + + @Override + public List list() { + out.println("EEEK list()"); + return null; + } + + @Override + public Map map() { + out.println("EEEK map()"); + return null; + } + + @Override + public Object get(int arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void set(int arg0, Object arg1) { + // TODO Auto-generated method stub + + } + +}; diff --git a/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java b/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java new file mode 100644 index 00000000..92c7f398 --- /dev/null +++ b/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java @@ -0,0 +1,266 @@ +package org.oscim.theme.carto; + +import static java.lang.System.out; +import static org.jeo.map.CartoCSS.BACKGROUND_COLOR; +import static org.jeo.map.CartoCSS.OPACITY; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.jeo.carto.Carto; +import org.jeo.map.CartoCSS; +import org.jeo.map.RGB; +import org.jeo.map.Rule; +import org.jeo.map.RuleList; +import org.jeo.map.Style; +import org.oscim.core.GeometryBuffer.GeometryType; +import org.oscim.core.MapElement; +import org.oscim.core.Tag; +import org.oscim.core.TagSet; +import org.oscim.theme.IRenderTheme; +import org.oscim.theme.styles.Area; +import org.oscim.theme.styles.Line; +import org.oscim.theme.styles.RenderStyle; + +public class RenderTheme implements IRenderTheme { + + final String STYLE = "" + + + "[building = 'yes'] {" + + " z: 1;" + + " polygon-fill: #eee;" + + " [zoom >= 16] {" + + " polygon-fill: #c00;" + + " }" + + "}" + + + "[admin_level = '2'] {" + + " line-color: #000;" + + " line-width: 1;" + + " z: 1;" + + "}" + + + "[admin_level = '2'] {" + + " line-color: #000;" + + " line-width: 1;" + + " z: 1;" + + "}" + + + "[admin_level = '4'] {" + + " line-color: #aaa;" + + " line-width: 1;" + + " z: 2;" + + "}" + + + "[highway = 'motorway'] {" + + " line-color: #a00;" + + " z: 10;" + + "}" + + + "[highway = 'primary'] {" + + " line-color: #aa0;" + + " z: 11;" + + "}" + + + "[highway = 'residential'],[highway = 'road'],[highway = 'secondary'] {" + + " line-color: #fff;" + + " z: 12;" + + "}" + + + " [landuse = 'forest'] {" + + " polygon-fill: #0a0;" + + " z: 2;" + + "}" + + + "[natural = 'water'] {" + + " polygon-fill: #00a;" + + " z: 3;" + + "}"; + + private Style mStyle; + private RuleList mRules; + + MatcherFeature mMatchFeature = new MatcherFeature(); + private int mBackground; + + public RenderTheme() { + + try { + mStyle = loadStyle(); + } catch (IOException e) { + e.printStackTrace(); + } + + // get map background + RuleList rules = mStyle.getRules().selectByName("Map", false); + if (!rules.isEmpty()) { + Rule rule = rules.collapse(); + RGB bgColor = rule.color(null, BACKGROUND_COLOR, null); + if (bgColor != null) { + bgColor = bgColor.alpha(rule.number(null, OPACITY, 1f)); + mBackground = color(bgColor); + } + } + + mRules = mStyle.getRules(); + + //out.println(mRules); + //out.println(); + if (mRules.get(1).equals(mRules.get(2))) + out.println("ok"); + + for (Rule r : mRules) + out.println(formatRule(r, 0)); + } + + class StyleSet { + int level; + RenderStyle[] ri = new RenderStyle[2]; + } + + Map mStyleSets = new HashMap(); + int mCurLevel = 0; + + public String formatRule(Rule r, int indent) { + StringBuilder sb = new StringBuilder(); + + String pad = ""; + for (int i = 0; i < indent; i++) + pad += " "; + + sb.append(pad); + + if (sb.length() > 0) + sb.setLength(sb.length() - 1); + + sb.append(pad).append(" {").append("\n"); + + StyleSet s = new StyleSet(); + RGB l = null; + RGB p = null; + if (r.properties().containsKey(CartoCSS.LINE_COLOR)) { + l = r.color(null, CartoCSS.LINE_COLOR, RGB.black); + } + if (r.properties().containsKey(CartoCSS.POLYGON_FILL)) { + p = r.color(null, CartoCSS.POLYGON_FILL, RGB.black); + } + + if (p != null) { + s.ri[0] = new Area(mCurLevel++, color(p)); + } + + if (l != null) { + s.ri[1] = new Line(mCurLevel++, color(l), 1); + } + + if (p != null || l != null) { + mStyleSets.put(r, s); + out.println("put " + s.ri[0] + s.ri[1]); + } + + for (Map.Entry e : r.properties().entrySet()) { + sb.append(pad).append(" ").append(e.getKey()).append(": ").append(e.getValue()) + .append(";\n"); + } + + for (Rule nested : r.nested()) { + sb.append(formatRule(nested, indent + 2)).append("\n"); + } + + sb.append(pad).append("}"); + return sb.toString(); + } + + Style loadStyle() throws IOException { + return Carto.parse(STYLE); + } + + @Override + public synchronized RenderStyle[] matchElement(GeometryType type, TagSet tags, + int zoomLevel) { + MatcherFeature f = mMatchFeature; + + f.setTags(tags); + f.setZoom(zoomLevel); + + RuleList rules = mRules.match(f); + + Rule r = rules.collapse(); + + //out.println(r); + if (rules.isEmpty()) + return null; + + int z = r.number(f, "z", 0f).intValue(); + + if (type == GeometryType.POLY) { + RGB c = r.color(f, CartoCSS.POLYGON_FILL, RGB.black); + out.println(z + " " + c); + return new RenderStyle[] { + new Area(z, color(c)) + }; + + } else if (type == GeometryType.LINE) { + RGB c = r.color(f, CartoCSS.LINE_COLOR, RGB.black); + float width = r.number(f, CartoCSS.LINE_WIDTH, 2f); + //out.println(z + " " + c); + + return new RenderStyle[] { + new Line(100 + z, color(c), width) + }; + + } else if (type == GeometryType.POINT) { + //RGB c = r.color(f, CartoCSS.MARKER_FILL, RGB.black); + //out.println(c); + //return new RenderInstruction[] { + // new Caption(color(c), width) + //}; + } + + return null; + } + + public static int color(RGB rgb) { + return rgb.getAlpha() << 24 + | rgb.getRed() << 16 + | rgb.getGreen() << 8 + | rgb.getBlue(); + } + + @Override + public void destroy() { + } + + @Override + public int getLevels() { + return 1; + } + + @Override + public int getMapBackground() { + return mBackground; + } + + @Override + public void scaleTextSize(float scaleFactor) { + } + + public static void main(String[] args) { + RenderTheme t = new RenderTheme(); + + MapElement e = new MapElement(); + e.startPolygon(); + e.tags.add(new Tag("building", "yes")); + + t.matchElement(GeometryType.POLY, e.tags, 16); + t.matchElement(GeometryType.POLY, e.tags, 15); + } + + @Override + public void updateInstructions() { + // TODO Auto-generated method stub + + } + +}