package it.geosolutions.android.map.renderer;
import it.geosolutions.android.map.model.Layer;
import it.geosolutions.android.map.model.Source;
import it.geosolutions.android.map.utils.RendererProvider;
import java.util.ArrayList;
import java.util.Iterator;
import org.mapsforge.android.maps.Projection;
import org.mapsforge.core.model.BoundingBox;
import android.graphics.Canvas;
import android.util.Log;
/**
* use multiple renderers to render data from different sources
* @author Lorenzo Natali
*
*/
public class MultiSourceRenderer implements OverlayRenderer<Layer> {
private ArrayList<Layer> layers =new ArrayList<Layer>();
private ArrayList<OverlayRenderer> renderers = new ArrayList<OverlayRenderer>();
private Projection projection;
public void setLayers(ArrayList<Layer> layers){
synchronized(this.layers){
this.layers =layers;
}
synchronized (renderers) {
updateRenderers();
}
}
private void updateRenderers() {
renderers = new ArrayList<OverlayRenderer>();
ArrayList<Layer> layerChunk =new ArrayList<Layer>();
Iterator<Layer> iterator = this.layers.iterator();
//create chunks
Layer l = null;
Source s = null;
if(iterator.hasNext()){
l = iterator.next();
s = l.getSource();
//only one layer case
if(!iterator.hasNext()){
layerChunk.add(l);
generateRenderer(layerChunk, s);
}
}
while (iterator.hasNext()){
layerChunk.add(l);
if(!iterator.hasNext()){
layerChunk.add(l);
generateRenderer(layerChunk, s);
}
//get all next layers with the same source
while(iterator.hasNext()){
l = iterator.next();
if(l.getSource() == null){
Log.e("MultiSourceRenderer", "Missing source for "+l.getTitle());
continue;
}
if(l.getSource().equals(s)){//TODO check if equals method have to be redefined
layerChunk.add(l);
if (!iterator.hasNext()){
//generate last renderer and chunk
generateRenderer(layerChunk, s);
layerChunk =new ArrayList<Layer>();
}
}else{
//here we have the layer chunk
generateRenderer(layerChunk, s);
//Start new Chunk
s = l.getSource();
layerChunk =new ArrayList<Layer>();
//if the list is finished, the last
//renderer have to be generated
if(!iterator.hasNext()){
layerChunk.add(l);
generateRenderer(layerChunk, s);
}
break;
}
}
}
Log.v("Renderer","Created renderers:"+renderers.size());
}
/**
* Generates a renderer for a list of layers from the same source
* @param layerChunk the list of layer that have to use this renderer
* @param s a <Source> implementation to choose the proper renderer
*/
private void generateRenderer(ArrayList<Layer> layerChunk, Source s) {
OverlayRenderer r = RendererProvider.getRenderer(s);
r.setProjection(projection);
r.setLayers(layerChunk);
synchronized (renderers) {
renderers.add(r);
}
}
/**
* Renders the layers associated to the renerer calling his internal
* <OverlayRenderer> objects
* @throws RenderingException
*/
public void render(Canvas c, BoundingBox boundingBox, byte zoomLevel) throws RenderingException {
//TODO add a method to prepare request to start them at the same time
//the render methods will wait
//render anyway
RenderingException lastException =null;
synchronized (renderers) {
for(OverlayRenderer r : renderers){
try{
r.render(c, boundingBox, zoomLevel);
}catch(RenderingException e){
lastException = e;
}
}
}
//notify only the last exception
if(lastException !=null){
throw lastException;
}
}
/**
* Refresh the layers
* @param layer
*/
public void refreshLayer(Layer layer) {
synchronized (renderers) {
for(OverlayRenderer r:renderers){
if(r.getLayers() != null && r.getLayers().contains(layer)){
r.refresh();
return;
}
}
}
}
@Override
public ArrayList<Layer> getLayers() {
synchronized (layers){
return layers;
}
}
@Override
public void refresh() {
synchronized (renderers) {
for(OverlayRenderer<?> r:renderers){
r.refresh();
}
}
}
@Override
public void setProjection(Projection projection) {
this.projection = projection;
}
}