/* * Copyright 2013 Hannes Janetzek * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package org.oscim.layers.tile.vector; import org.oscim.core.MapElement; import org.oscim.layers.tile.MapTile; import org.oscim.layers.tile.TileLayer; import org.oscim.layers.tile.TileLoader; import org.oscim.layers.tile.TileManager; import org.oscim.layers.tile.VectorTileRenderer; import org.oscim.map.Map; import org.oscim.renderer.bucket.RenderBuckets; import org.oscim.theme.IRenderTheme; import org.oscim.theme.styles.RenderStyle; import org.oscim.tiling.TileSource; import org.oscim.tiling.TileSource.OpenResult; import org.oscim.utils.pool.Inlist.List; import org.oscim.utils.pool.LList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The vector-tile-map layer. This class manages instances of * {@link VectorTileLoader} that load and assemble vector tiles * for rendering. */ public class VectorTileLayer extends TileLayer { static final Logger log = LoggerFactory.getLogger(VectorTileLayer.class); protected TileSource mTileSource; public VectorTileLayer(Map map, TileSource tileSource) { this(map, new TileManager(map, 100), new VectorTileRenderer()); setTileSource(tileSource); } public VectorTileLayer(Map map, int cacheLimit) { this(map, new TileManager(map, cacheLimit), new VectorTileRenderer()); } public VectorTileLayer(Map map, TileManager tileManager, VectorTileRenderer renderer) { super(map, tileManager, renderer); initLoader(getNumLoaders()); } @Override protected TileLoader createLoader() { return new VectorTileLoader(this); } /** * Sets the {@link TileSource} used by {@link TileLoader}. * * @return true when new TileSource was set (has changed) */ public boolean setTileSource(TileSource tileSource) { pauseLoaders(true); mTileManager.clearJobs(); mTileManager.setZoomLevel(tileSource.getZoomLevelMin(), tileSource.getZoomLevelMax()); if (mTileSource != null) { mTileSource.close(); mTileSource = null; } OpenResult msg = tileSource.open(); if (msg != OpenResult.SUCCESS) { log.debug(msg.getErrorMessage()); return false; } mTileSource = tileSource; for (TileLoader l : mTileLoader) ((VectorTileLoader) l).setDataSource(tileSource.getDataSource()); mMap.clearMap(); resumeLoaders(); return true; } /** * Set {@link IRenderTheme} used by {@link TileLoader} */ public void setRenderTheme(IRenderTheme theme) { /* wait for loaders to finish all current jobs to * not change theme instance hold by loader instance * while running */ pauseLoaders(true); mTileManager.clearJobs(); mTheme = theme; // for (TileLoader l : mTileLoader) // ((VectorTileLoader) l).setRenderTheme(theme); tileRenderer().setOverdrawColor(theme.getMapBackground()); resumeLoaders(); } private IRenderTheme mTheme; public IRenderTheme getTheme() { return mTheme; } /** * Hook to intercept tile data processing. Called concurently by tile * loader threads, so dont keep tile specific state. */ public interface TileLoaderProcessHook { public boolean process(MapTile tile, RenderBuckets layers, MapElement element); /** Called on loader thread when tile loading is completed */ public void complete(MapTile tile, boolean success); } /** * Hook to intercept tile data processing after theme style lookup. Called * concurently by tile loader threads, so dont keep tile specific state. See * e.g. LabelTileLoaderHook. */ public interface TileLoaderThemeHook { /** Called for each RenderStyle found for a MapElement. */ public boolean render(MapTile tile, RenderBuckets buckets, MapElement element, RenderStyle style, int level); /** Called on loader thread when tile loading is completed */ public void complete(MapTile tile, boolean success); } private List<LList<TileLoaderProcessHook>> mLoaderProcessHooks = new List<LList<TileLoaderProcessHook>>(); private List<LList<TileLoaderThemeHook>> mLoaderThemeHooks = new List<LList<TileLoaderThemeHook>>(); public void addHook(TileLoaderProcessHook h) { mLoaderProcessHooks.append(new LList<TileLoaderProcessHook>(h)); } public void addHook(TileLoaderThemeHook h) { mLoaderThemeHooks.append(new LList<TileLoaderThemeHook>(h)); } @Override public void onDetach() { super.onDetach(); mTileSource.close(); } public void callThemeHooks(MapTile tile, RenderBuckets layers, MapElement element, RenderStyle style, int level) { LList<TileLoaderThemeHook> th = mLoaderThemeHooks.head(); while (th != null) { if (th.data.render(tile, layers, element, style, level)) return; th = th.next; } } public boolean callProcessHooks(MapTile tile, RenderBuckets layers, MapElement element) { LList<TileLoaderProcessHook> ph = mLoaderProcessHooks.head(); while (ph != null) { if (ph.data.process(tile, layers, element)) return true; ph = ph.next; } return false; } public void callHooksComplete(MapTile tile, boolean success) { /* NB: cannot use internal iterater as this function * is called concurently by TileLoaders */ LList<TileLoaderThemeHook> th = mLoaderThemeHooks.head(); while (th != null) { th.data.complete(tile, success); th = th.next; } LList<TileLoaderProcessHook> ph = mLoaderProcessHooks.head(); while (ph != null) { ph.data.complete(tile, success); ph = ph.next; } } }