/*
* GeoSolutions map - Digital field mapping on Android based devices
* Copyright (C) 2013 GeoSolutions (www.geo-solutions.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package it.geosolutions.android.map.wms.renderer;
import it.geosolutions.android.map.R;
import it.geosolutions.android.map.renderer.RenderingException;
import it.geosolutions.android.map.utils.ProjectionUtils;
import it.geosolutions.android.map.wms.WMSLayer;
import it.geosolutions.android.map.wms.WMSLayerChunker;
import it.geosolutions.android.map.wms.WMSRequest;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import org.mapsforge.android.maps.Projection;
import org.mapsforge.core.model.BoundingBox;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;
/**
* A Simple renderer that draws maps
* @author Lorenzo Natali (lorenzo.natali@geo-solutions.it)
*
*/
public class WMSUntiledRenderer implements WMSRenderer{
ArrayList<WMSRequest> requests;
private ArrayList<WMSLayer> layers;
private Projection projection;
private int status =0;
private HttpURLConnection connection;
public void render(Canvas c, BoundingBox boundingBox, byte zoomLevel) throws RenderingException{
if(requests ==null){
Log.d("WMS","request is missing, draw skipped");
return;
}
for(WMSRequest r :requests){
URL url = r.getURL(createParameters(c,boundingBox,zoomLevel));
draw(c,url,boundingBox,zoomLevel);
}
}
/**
* Draws the layers on the canvas from a WMS url
* @param c
* @param url
* @param zoomLevel
* @param boundingBox
* @throws RenderingException
*/
private void draw(Canvas c, URL url, BoundingBox boundingBox, byte zoomLevel) throws RenderingException {
if(url == null) return; //TODO notify
if(connection !=null){
connection.disconnect();
}
try {
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(1000);//TODO Make this configurable
} catch (IOException e2) {
Log.e("WMS","error opening connection");
notifyError(e2);
return;
}
InputStream is;
try {
is = connection.getInputStream();
Bitmap img = BitmapFactory.decodeStream(is);
if(img!=null){
long[] pxDp= ProjectionUtils.getMapLeftTopPoint(projection);
c.drawBitmap(img, pxDp[0] >0 ? pxDp[0] : 0 , pxDp[1] >0 ? pxDp[1] : 0, null);
if(Log.isLoggable("WMS", Log.VERBOSE)){
Log.v("WMS","Draw downloaded bitmap for "+ layers.size() + "layers from" +url.getHost());
}
notifySuccess();
}else {
Log.e("WMS","null image from the request");
}
} catch (IOException e1) {
notifyError(e1);
Log.e("WMS","unable to read from the wms service for url"+url);
}finally{
//if the status has changed during draw. the exception is raised
if(this.status != 0){
throw new RenderingException(this.status);
}
}
}
/**
* Create the parameters for the current location and the default ones too
* @param c the Canvas
* @param boundingBox
* @param zoomLevel
* @return
*/
private HashMap<String,String> createParameters(Canvas c, BoundingBox boundingBox, byte zoomLevel){
double n = boundingBox.maxLatitude;
double w = boundingBox.minLongitude;
double s = boundingBox.minLatitude;
double e = boundingBox.maxLongitude;
double nm = ProjectionUtils.toWebMercatorY(n);
double wm = ProjectionUtils.toWebMercatorX(w);
double sm =ProjectionUtils.toWebMercatorY(s);
double em = ProjectionUtils.toWebMercatorX(e);
HashMap<String,String> params = new HashMap<String,String>();
//picture size
synchronized (projection) {
long[] pictureSize = ProjectionUtils.calculateMapSize(c.getWidth(), c.getHeight(), projection);
params.put("width",(pictureSize[0])+"");
params.put("height",(pictureSize[1])+"");
};
//Log.v("WMSRenderer","request bbox:"+w+","+s+""+e +","+n);
params.put("bbox", wm + "," + sm + "," + em + "," + nm);
params.put("service","WMS");
params.put("srs","EPSG:900913");
params.put("request","GetMap");
params.put("version","1.1.1");
return params;
}
@Override
public void setLayers(ArrayList layers) {
this.layers = layers;
refresh();
}
@Override
public void refresh() {
requests = WMSLayerChunker.createChunkedRequests(this.layers);
}
@Override
public ArrayList<WMSLayer> getLayers() {
// TODO Auto-generated method stub
return layers;
}
@Override
public void setProjection(Projection projection) {
this.projection = projection;
}
/* (non-Javadoc)
* @see it.geosolutions.android.map.wms.renderer.WMSRenderer#notifyError(java.lang.Exception)
*/
@Override
public void notifyError(Exception e) {
if(e instanceof UnknownHostException){
this.status = R.string.error_connectivity_problem;
}else{
this.status = R.string.error_rendering;
}
for(WMSLayer l : layers){
l.setStatus(this.status);
}
}
/* (non-Javadoc)
* @see it.geosolutions.android.map.wms.renderer.WMSRenderer#notifySuccess()
*/
@Override
public void notifySuccess() {
if(status!=0){
status = 0;
for(WMSLayer l : layers){
l.setStatus(0);
}
}
}
}