From 3fd92982c4b86123270526f608079d72ead0a250 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek <hannes.janetzek@gmail.com> Date: Tue, 21 Jan 2014 18:21:15 +0100 Subject: [PATCH] add examples --- vtm-jeo-android/AndroidManifest.xml | 30 ++ vtm-jeo-android/project.properties | 15 + vtm-jeo-android/res/drawable/ic_launcher.png | Bin 0 -> 42193 bytes vtm-jeo-android/res/layout/activity_map.xml | 93 +++++ .../org/oscim/jeo/android/TestActivity.java | 162 ++++++++ .../src/org/oscim/jeo/test/LayerTest.java | 27 ++ .../src/org/oscim/jeo/test/ThemeTest.java | 34 ++ vtm-jeo/src/org/oscim/jeo/JeoUtils.java | 12 + vtm-jeo/src/org/oscim/layers/JeoMapLayer.java | 80 ---- .../src/org/oscim/layers/JeoMapLoader.java | 346 ------------------ .../src/org/oscim/layers/JeoTileLayer.java | 35 ++ .../src/org/oscim/layers/JeoTileSource.java | 80 ++++ .../src/org/oscim/layers/JeoVectorLayer.java | 150 ++++++++ vtm-jeo/src/org/oscim/layers/JtsLayer.java | 102 ++++++ .../src/org/oscim/layers/OSMIndoorLayer.java | 141 +++++++ .../JeoTestData.java => test/JeoTest.java} | 69 +++- .../org/oscim/theme/carto/RenderTheme.java | 26 +- .../src/org/oscim/theme/carto/RuleDebug.java | 74 ---- 18 files changed, 956 insertions(+), 520 deletions(-) create mode 100644 vtm-jeo-android/AndroidManifest.xml create mode 100644 vtm-jeo-android/project.properties create mode 100644 vtm-jeo-android/res/drawable/ic_launcher.png create mode 100644 vtm-jeo-android/res/layout/activity_map.xml create mode 100644 vtm-jeo-android/src/org/oscim/jeo/android/TestActivity.java create mode 100644 vtm-jeo-desktop/src/org/oscim/jeo/test/LayerTest.java create mode 100644 vtm-jeo-desktop/src/org/oscim/jeo/test/ThemeTest.java create mode 100644 vtm-jeo/src/org/oscim/jeo/JeoUtils.java delete mode 100644 vtm-jeo/src/org/oscim/layers/JeoMapLayer.java delete mode 100644 vtm-jeo/src/org/oscim/layers/JeoMapLoader.java create mode 100644 vtm-jeo/src/org/oscim/layers/JeoTileLayer.java create mode 100644 vtm-jeo/src/org/oscim/layers/JeoTileSource.java create mode 100644 vtm-jeo/src/org/oscim/layers/JeoVectorLayer.java create mode 100644 vtm-jeo/src/org/oscim/layers/JtsLayer.java create mode 100644 vtm-jeo/src/org/oscim/layers/OSMIndoorLayer.java rename vtm-jeo/src/org/oscim/{layers/JeoTestData.java => test/JeoTest.java} (60%) delete mode 100644 vtm-jeo/src/org/oscim/theme/carto/RuleDebug.java diff --git a/vtm-jeo-android/AndroidManifest.xml b/vtm-jeo-android/AndroidManifest.xml new file mode 100644 index 00000000..d1f57c85 --- /dev/null +++ b/vtm-jeo-android/AndroidManifest.xml @@ -0,0 +1,30 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.oscim.jeo.android" + android:versionCode="1" + android:versionName="1.0" > + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <uses-sdk + android:minSdkVersion="10" + android:targetSdkVersion="18" /> + + <application + android:label="JeoMap" + android:icon="@drawable/ic_launcher" + android:allowBackup="true" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" > + <activity + android:name=".TestActivity" + android:label="JeoMap" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> \ 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..b351fa60 --- /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-17 +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 0000000000000000000000000000000000000000..6496c5adfe925704de11b80a821346331a02dd91 GIT binary patch literal 42193 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�<)dlA2mCAeD-`Sy@pj+h2DZ*9a%z(shpNR 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 literal 0 HcmV?d00001 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 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" > + + <org.oscim.android.MapView + android:id="@+id/mapView" + android:layout_width="fill_parent" + android:layout_height="fill_parent" > + </org.oscim.android.MapView> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:alpha="0.7" + android:gravity="center" + android:orientation="vertical" > + + <ToggleButton + android:id="@+id/level7" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="7" + android:textOn="7" /> + + <ToggleButton + android:id="@+id/level6" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="6" + android:textOn="6" /> + + <ToggleButton + android:id="@+id/level5" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="5" + android:textOn="5" /> + + <ToggleButton + android:id="@+id/level4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="4" + android:textOn="4" /> + + <ToggleButton + android:id="@+id/level3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="3" + android:textOn="3" /> + + <ToggleButton + android:id="@+id/level2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="2" + android:textOn="2" /> + + <ToggleButton + android:id="@+id/level1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="1" + android:textOn="1" /> + + <ToggleButton + android:id="@+id/level0" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="0" + android:textOn="0" /> + + <ToggleButton + android:id="@+id/level_u1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onClick" + android:textOff="-1" + android:textOn="-1" /> + </LinearLayout> + +</RelativeLayout> \ 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..8128a707 --- /dev/null +++ b/vtm-jeo-android/src/org/oscim/jeo/android/TestActivity.java @@ -0,0 +1,162 @@ +package org.oscim.jeo.android; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +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.os.Environment; +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); + + //String PATH = "http://opensciencemap.org/featureserver/featureserver.cgi/osm_indoor"; + + // from http://overpass-turbo.eu/s/2vp + String PATH = "https://gist.github.com/hjanetzek/8959418/raw/overpass.geojson"; + //String PATH = "https://gist.github.com/anonymous/8960337/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/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/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/JeoMapLayer.java b/vtm-jeo/src/org/oscim/layers/JeoMapLayer.java deleted file mode 100644 index 083a11f0..00000000 --- a/vtm-jeo/src/org/oscim/layers/JeoMapLayer.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.oscim.layers; - -import org.jeo.data.Dataset; -import org.jeo.map.Style; -import org.oscim.core.MapPosition; -import org.oscim.layers.JeoMapLoader.Task; -import org.oscim.map.Map; -import org.oscim.map.Map.UpdateListener; -import org.oscim.renderer.ElementRenderer; -import org.oscim.renderer.MapRenderer.Matrices; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class JeoMapLayer extends Layer implements UpdateListener { - - public static final Logger log = LoggerFactory.getLogger(JeoMapLayer.class); - - final org.jeo.map.View view; - private final org.jeo.map.Map mJeoMap; - - private final JeoMapLoader mWorker; - - public JeoMapLayer(Map map, Dataset data, Style style) { - super(map); - - mJeoMap = org.jeo.map.Map.build().layer(data).style(style).map(); - view = mJeoMap.getView(); - - mRenderer = new ElementRenderer() { - @Override - protected synchronized void update(MapPosition position, boolean changed, - Matrices matrices) { - - if (mNewLayers != null) { - mMapPosition.copy(mNewLayers); - - this.layers.clear(); - this.layers.baseLayers = mNewLayers.layers; - mNewLayers = null; - - compile(); - log.debug("is ready " + isReady() + " " + layers.getSize()); - } - } - }; - - mWorker = new JeoMapLoader(this); - mWorker.start(); - } - - @Override - public void onDetach() { - super.onDetach(); - - mWorker.awaitPausing(); - try { - mWorker.join(); - } catch (Exception e) { - log.error(e.toString()); - } - } - - @Override - public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) { - if (changed) { - log.debug("go"); - mWorker.go(); - } - } - - Task mNewLayers; - - void setLayers(Task newLayers) { - synchronized (mRenderer) { - mNewLayers = newLayers; - } - mMap.render(); - } - -} diff --git a/vtm-jeo/src/org/oscim/layers/JeoMapLoader.java b/vtm-jeo/src/org/oscim/layers/JeoMapLoader.java deleted file mode 100644 index ad7da174..00000000 --- a/vtm-jeo/src/org/oscim/layers/JeoMapLoader.java +++ /dev/null @@ -1,346 +0,0 @@ -package org.oscim.layers; - -// FIXME -// Apache License 2.0 - -import java.io.IOException; - -import org.jeo.data.Dataset; -import org.jeo.data.Query; -import org.jeo.data.VectorDataset; -import org.jeo.feature.Feature; -import org.jeo.geom.CoordinatePath; -import org.jeo.geom.Envelopes; -import org.jeo.geom.Geom; -import org.jeo.map.CartoCSS; -import org.jeo.map.Map; -import org.jeo.map.RGB; -import org.jeo.map.Rule; -import org.jeo.map.RuleList; -import org.jeo.map.View; -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.renderer.elements.ElementLayers; -import org.oscim.renderer.elements.LineLayer; -import org.oscim.renderer.elements.MeshLayer; -import org.oscim.renderer.elements.RenderElement; -import org.oscim.theme.renderinstruction.Line; -import org.oscim.utils.PausableThread; -import org.oscim.utils.TileClipper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.vividsolutions.jts.geom.Coordinate; -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; - -/** - * Does the work of actually rendering the map, outside of the ui thread. - * - * @author Justin Deoliveira, OpenGeo - * @author Hannes Janetzek, OpenScienceMap - */ - -public class JeoMapLoader extends PausableThread { - - static final Logger log = LoggerFactory.getLogger(JeoMapLoader.class); - - private final JeoMapLayer mMapLayer; - - public JeoMapLoader(JeoMapLayer mapLayer) { - mMapLayer = mapLayer; - } - - private ElementLayers layers; - private final GeometryBuffer mGeom = new GeometryBuffer(128, 4); - - private Task mCurrentTask; - - private double mMinX; - private double mMinY; - - @Override - protected void doWork() throws InterruptedException { - log.debug("start"); - mWork = false; - Envelope env = new Envelope(); - BoundingBox bbox = mMapLayer.mMap.getViewport().getViewBox(); - - env.init(bbox.getMinLongitude(), bbox.getMaxLongitude(), - bbox.getMinLatitude(), bbox.getMaxLatitude()); - int w = mMapLayer.mMap.getWidth(); - int h = mMapLayer.mMap.getHeight(); - mMapLayer.view.setWidth(w); - mMapLayer.view.setHeight(h); - - mClipper.setRect(-w, -h, w, h); - - mMapLayer.view.zoomto(env); - - Task task = new Task(); - task.view = mMapLayer.view.clone(); - - mMapLayer.mMap.getMapPosition(task); - - mCurrentTask = task; - layers = new ElementLayers(); - - Envelope b = task.view.getBounds(); - - // reduce lines points min distance - mMinX = ((b.getMaxX() - b.getMinX()) / task.view.getWidth()) * 2; - mMinY = ((b.getMaxY() - b.getMinY()) / task.view.getHeight()) * 2; - - Map map = mMapLayer.view.getMap(); - - for (org.jeo.map.Layer l : map.getLayers()) { - - if (!l.isVisible()) - continue; - - Dataset data = l.getData(); - - RuleList rules = - map.getStyle().getRules().selectById(l.getName(), true).flatten(); - - log.debug("data {}", data); - - if (data instanceof VectorDataset) { - for (RuleList ruleList : rules.zgroup()) { - render(task.view, (VectorDataset) data, ruleList); - } - } - } - - if (layers.baseLayers != null) { - mCurrentTask.layers = layers.baseLayers; - - //layers.baseLayers = null; - //layers.clear(); - - mMapLayer.setLayers(mCurrentTask); - } - layers = null; - mCurrentTask = null; - - } - - void render(View view, VectorDataset data, RuleList rules) { - - try { - Query q = new Query().bounds(view.getBounds()); - log.debug("query {}", q); - - // reproject - // if (data.getCRS() != null) { - // if (!Proj.equal(view.getCRS(), data.getCRS())) { - // q.reproject(view.getCRS()); - // } - //} - //else { - // log.debug("Layer " + data.getName() - // + " specifies no projection, assuming map projection"); - //} - - for (Feature f : data.cursor(q)) { - - RuleList rs = rules.match(f); - if (rs.isEmpty()) { - continue; - } - - Rule r = rules.match(f).collapse(); - if (r == null) - continue; - - draw(view, f, r); - } - } catch (IOException e) { - log.error("Error querying layer " + data.getName() + e); - } - } - - Geometry clipGeometry(View view, Geometry g) { - // TODO: doing a full intersection is sub-optimal, - // look at a more efficient clipping - // algorithm, like cohen-sutherland - return g.intersection(Envelopes.toPolygon(view.getBounds())); - } - - void draw(View view, Feature f, Rule rule) { - Geometry g = f.geometry(); - if (g == null) { - return; - } - - // g = clipGeometry(view, g); - // if (g.isEmpty()) { - // return; - // } - - switch (Geom.Type.from(g)) { - case POINT: - case MULTIPOINT: - //log.debug("draw point"); - //drawPoint(f, rule); - return; - case LINESTRING: - case MULTILINESTRING: - //log.debug("draw line"); - drawLine(f, rule, g); - return; - case POLYGON: - //Polygon p = (Polygon) g; - //p.reverse(); - //log.debug("draw polygon"); - drawPolygon(f, rule, g); - return; - - case MULTIPOLYGON: - //log.debug("draw polygon"); - for (int i = 0, n = g.getNumGeometries(); i < n; i++) - drawPolygon(f, rule, g.getGeometryN(i)); - return; - default: - throw new UnsupportedOperationException(); - } - } - - private void drawLine(Feature f, Rule rule, Geometry g) { - - LineLayer ll = layers.getLineLayer(0); - - 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, color(color), width); - ll.width = width; - } - - mGeom.clear(); - mGeom.startLine(); - - CoordinatePath p = CoordinatePath.create(g); - path(mGeom, p); - - //log.debug( ll.width + " add line " + mGeom.pointPos + " " + Arrays.toString(mGeom.points)); - - ll.addLine(mGeom); - } - - TileClipper mClipper = new TileClipper(0, 0, 0, 0); - - private void drawPolygon(Feature f, Rule rule, Geometry g) { - - LineLayer ll = layers.getLineLayer(3); - - if (ll.line == null) { - RGB color = rule.color(f, CartoCSS.POLYGON_FILL, RGB.red); - float width = rule.number(f, CartoCSS.LINE_WIDTH, 1.2f); - ll.line = new Line(2, color(color), width); - ll.width = width; - } - - //PolygonLayer pl = layers.getPolygonLayer(1); - // - //if (pl.area == null) { - // RGB color = rule.color(f, CartoCSS.POLYGON_FILL, RGB.red); - // pl.area = new Area(1, color(color)); - //} - - MeshLayer mesh = layers.getMeshLayer(2); - - mGeom.clear(); - mGeom.startPolygon(); - //mGeom.startLine(); - - CoordinatePath p = CoordinatePath.create(g).generalize(mMinX, mMinY); - if (path(mGeom, p) < 3) - return; - - if (!mClipper.clip(mGeom)) - return; - - //log.debug(ll.width + " add poly " + mGeom.pointPos + " " + Arrays.toString(mGeom.points)); - mesh.addMesh(mGeom); - - ll.addLine(mGeom); - //pl.addPolygon(mGeom.points, mGeom.index); - } - - public static int color(RGB rgb) { - return rgb.getAlpha() << 24 - | rgb.getRed() << 16 - | rgb.getGreen() << 8 - | rgb.getBlue(); - } - - private int path(GeometryBuffer g, CoordinatePath path) { - - MapPosition pos = mCurrentTask; - double scale = pos.scale * Tile.SIZE; - 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; - } - - @Override - protected String getThreadName() { - return "JeoMapLayer"; - } - - @Override - protected boolean hasWork() { - return mWork; - } - - boolean mWork; - - public void go() { - if (hasWork()) - return; - - mWork = true; - - synchronized (this) { - notifyAll(); - } - } - - static class Task extends MapPosition { - View view; - RenderElement layers; - } -} 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<Geometry> { + + 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..e1f8d4c0 --- /dev/null +++ b/vtm-jeo/src/org/oscim/layers/OSMIndoorLayer.java @@ -0,0 +1,141 @@ +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); + + if (ll.line == null) { + float width = rule.number(f, CartoCSS.LINE_WIDTH, 1.2f); + int color = Color.rainbow((level + 1) / 10f); + + if (level > -2 && !activeLevels[level + 1]) + 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 && !activeLevels[level + 1]) + 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); + + 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<String, Object> tags = (HashMap<String, Object>) o; + @SuppressWarnings("unchecked") + HashMap<String, Object> reltags = (HashMap<String, Object>) 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/layers/JeoTestData.java b/vtm-jeo/src/org/oscim/test/JeoTest.java similarity index 60% rename from vtm-jeo/src/org/oscim/layers/JeoTestData.java rename to vtm-jeo/src/org/oscim/test/JeoTest.java index d2be9c73..d29955a3 100644 --- a/vtm-jeo/src/org/oscim/layers/JeoTestData.java +++ b/vtm-jeo/src/org/oscim/test/JeoTest.java @@ -1,11 +1,15 @@ -package org.oscim.layers; +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; @@ -13,24 +17,52 @@ 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 JeoTestData { +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("" + - "#things {" + + "#way {" + + " line-width: 2;" + " line-color: #c80;" + - " polygon-fill: #00a;" + + " polygon-fill: #44111111;" + + " " + "}" + "#states {" + - " polygon-fill: #0dc;" + + " line-width: 2.2;" + + " line-color: #c80;" + + " polygon-fill: #44111111;" + + " " + "}" ); @@ -41,6 +73,29 @@ public class JeoTestData { 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; @@ -53,6 +108,7 @@ public class JeoTestData { } if (memory) { + @SuppressWarnings("resource") MemWorkspace mem = new MemWorkspace(); //mem.put("layer", data); @@ -79,6 +135,7 @@ public class JeoTestData { 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) @@ -90,11 +147,9 @@ public class JeoTestData { try { data = mem.create(schema); } catch (UnsupportedOperationException e) { - // TODO Auto-generated catch block e.printStackTrace(); return null; } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); return null; } diff --git a/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java b/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java index 042a8794..92c7f398 100644 --- a/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java +++ b/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java @@ -13,16 +13,15 @@ import org.jeo.map.CartoCSS; import org.jeo.map.RGB; import org.jeo.map.Rule; import org.jeo.map.RuleList; -import org.jeo.map.Selector; 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.renderinstruction.Area; -import org.oscim.theme.renderinstruction.Line; -import org.oscim.theme.renderinstruction.RenderInstruction; +import org.oscim.theme.styles.Area; +import org.oscim.theme.styles.Line; +import org.oscim.theme.styles.RenderStyle; public class RenderTheme implements IRenderTheme { @@ -117,7 +116,7 @@ public class RenderTheme implements IRenderTheme { class StyleSet { int level; - RenderInstruction[] ri = new RenderInstruction[2]; + RenderStyle[] ri = new RenderStyle[2]; } Map<Rule, StyleSet> mStyleSets = new HashMap<Rule, StyleSet>(); @@ -132,11 +131,6 @@ public class RenderTheme implements IRenderTheme { sb.append(pad); - for (Selector s : r.getSelectors()) { - sb.append(RuleDebug.formatSelector(s)); - sb.append(","); - } - if (sb.length() > 0) sb.setLength(sb.length() - 1); @@ -183,7 +177,7 @@ public class RenderTheme implements IRenderTheme { } @Override - public synchronized RenderInstruction[] matchElement(GeometryType type, TagSet tags, + public synchronized RenderStyle[] matchElement(GeometryType type, TagSet tags, int zoomLevel) { MatcherFeature f = mMatchFeature; @@ -203,7 +197,7 @@ public class RenderTheme implements IRenderTheme { if (type == GeometryType.POLY) { RGB c = r.color(f, CartoCSS.POLYGON_FILL, RGB.black); out.println(z + " " + c); - return new RenderInstruction[] { + return new RenderStyle[] { new Area(z, color(c)) }; @@ -212,7 +206,7 @@ public class RenderTheme implements IRenderTheme { float width = r.number(f, CartoCSS.LINE_WIDTH, 2f); //out.println(z + " " + c); - return new RenderInstruction[] { + return new RenderStyle[] { new Line(100 + z, color(c), width) }; @@ -263,4 +257,10 @@ public class RenderTheme implements IRenderTheme { t.matchElement(GeometryType.POLY, e.tags, 15); } + @Override + public void updateInstructions() { + // TODO Auto-generated method stub + + } + } diff --git a/vtm-jeo/src/org/oscim/theme/carto/RuleDebug.java b/vtm-jeo/src/org/oscim/theme/carto/RuleDebug.java deleted file mode 100644 index ebc92865..00000000 --- a/vtm-jeo/src/org/oscim/theme/carto/RuleDebug.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.oscim.theme.carto; - -import static java.lang.System.out; - -import java.util.Map; - -import org.jeo.filter.Filter; -import org.jeo.map.Rule; -import org.jeo.map.Selector; - -public class RuleDebug { - - static void printRule(Rule r, int level) { - - out.println("> " + level + " >"); - out.println(formatRule(r, level)); - } - - public static String formatRule(Rule r, int indent) { - StringBuilder sb = new StringBuilder(); - String pad = ""; - for (int i = 0; i < indent; i++) { - pad += " "; - }; - - sb.append(pad); - for (Selector s : r.getSelectors()) { - sb.append(formatSelector(s)); - sb.append(","); - } - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - } - sb.append(pad).append(" {").append("\n"); - - for (Map.Entry<String, Object> e : r.properties().entrySet()) { - sb.append(pad).append(" ").append(e.getKey()).append(": ").append(e.getValue()) - .append(";\n"); - } - - for (Rule nested : r.nested()) { - sb.append(nested.toString(indent + 2)).append("\n"); - } - - sb.append(pad).append("}"); - return sb.toString(); - } - - public static String formatSelector(Selector s) { - StringBuffer sb = new StringBuffer(); - - if (s.getName() != null) { - sb.append(s.getName()); - } - if (s.getId() != null) { - sb.append("#").append(s.getId()); - } - for (String c : s.getClasses()) { - sb.append(".").append(c); - } - if (s.getFilter() != null && s.getFilter() != Filter.TRUE) { - sb.append("[").append(s.getFilter()).append("]"); - } - if (s.getAttachment() != null) { - sb.append("::").append(s.getAttachment()); - } - - if (s.isWildcard()) { - sb.append("*"); - } - - return sb.toString(); - } -}