OGC WPS publishing

OGC WPS publishing

We still have one more report to publish: Personalized HelloWorld.jrxml. Till now we had to point out our WMS and WFS source (used to be http://localhost:8080/geoserver), and since now we are entering directly into GeoServer we do not need to do that anymore. This approach changes the whole concept of georeport creation. Now we have to produce image and grab data directly on GeoServer and fill it in report, not sending just reference to data source anymore. This requires some changes in our report. Image will be sent as InputStream parameter. We are not using remote XML file as a datasource, so we have to query data using GeoServer built in methods and convert it to acceptable JasperReport data source. Quite a lot tasks and some new technologies.

Create new maven quickstart project named digmap, enter Group Id: hr.yottabyte.digmap and Artifact Id: digmap.  Now we need to include all the handy tools to create map on our own, and to grab the data from the datastore. We’ll use geotools library, jasper report and some basic geoserver elements. Add following dependency to your pom file:

		<dependency>
			<groupId>org.geoserver</groupId>
			<artifactId>main</artifactId>
			<version>2.5-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.geoserver</groupId>
			<artifactId>wms</artifactId>
			<version>2.5-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.geoserver</groupId>
			<artifactId>wfs</artifactId>
			<version>2.5-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.geoserver</groupId>
			<artifactId>platform</artifactId>
			<version>2.5-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>net.sf.jasperreports</groupId>
			<artifactId>jasperreports</artifactId>
			<version>5.6.0</version>
		</dependency>

		<dependency>
			<groupId>org.geotools</groupId>
			<artifactId>gt-cql</artifactId>
			<version>11.1</version>
		</dependency>
		<dependency>
			<groupId>org.geotools</groupId>
			<artifactId>gt-wms</artifactId>
			<version>11.1</version>
		</dependency>
		<dependency>
			<groupId>org.geotools</groupId>
			<artifactId>gt-process</artifactId>
			<version>11.1</version>
		</dependency>
		<dependency>
			<groupId>org.geotools</groupId>
			<artifactId>gt-geometry</artifactId>
			<version>11.1</version>
		</dependency>

and add some repositories to grab the dependency

		<repository>
			<id>maven2-repository.dev.java.net</id>
			<name>Java.net repository</name>
			<url>http://download.java.net/maven/2</url>
		</repository>
		<repository>
			<id>osgeo</id>
			<name>Open Source Geospatial Foundation Repository</name>
			<url>http://download.osgeo.org/webdav/geotools/</url>
		</repository>
		<repository>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
			<id>opengeo</id>
			<name>OpenGeo Maven Repository</name>
			<url>http://repo.opengeo.org</url>
		</repository>

Lets’s start with report changes. copy file PersonalizedHelloWorld.jrxml as PersonalizedHelloWorld4WPS.jrxml.  Add new parameter $P{wmsInputStream} and set it as a Image expression.  Change personalized message not to include greetings if userName is not set.

<parameter name="wmsInputStream" class="java.io.InputStream" isForPrompting="false"/>
...
				<textFieldExpression><![CDATA[($P{userName}.isEmpty()?"":("My name is "+$P{userName}+". "))+"I come from "+$P{countryName}+"."]]></textFieldExpression>

...
			<image>
				<reportElement x="0" y="0" width="555" height="125" uuid="2002c734-6652-40ce-a03c-2f9ecdf3daf8"/>
				<imageExpression><![CDATA[$P{wmsInputStream}]]></imageExpression>
			</image>

Somehow we have to get reference to geoserver catalog and wms service, and also we have to grab JasperReport compiled report from server. To be able to do such thing we have to add reference on those in applicationContext.xml (under project’s /src/main/resources/):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="digMapSimpleProcess" class="hr.yottabyte.digmap.DigMapSimpleProcess">
        <constructor-arg index="0" ref="catalog"/>
        <constructor-arg index="1" ref="wms"/>
        <constructor-arg index="2" ref="resourceLoader"/>
 </bean>
 <bean id="digMapProcess" class="hr.yottabyte.digmap.DigMapProcess">
        <constructor-arg index="0" ref="catalog"/>
        <constructor-arg index="1" ref="wms"/>
        <constructor-arg index="2" ref="resourceLoader"/>
 </bean>

 

We’ll create simple WPS process taking userName and countryName as parameters and returning report Well not exactly a report. In our case report is a PDF file. So one would expect to send request and download a PDF file. According to WPS specification, indeed,  WPS should be able to return everything, but current implementation is allowing to download only some image formats, not any kind of file.  There is an iniciative to implelemt FilePPIO to  enable WPS to return any kind of file open at CodeHause so give your vote for it. In out case we’ll just return absolute file path and open report on our own.

package hr.yottabyte.digmap;

import org.geoserver.catalog.Catalog;
import org.geoserver.wms.WMS;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.geotools.process.gs.GSProcess;
import org.springframework.core.io.ResourceLoader;

@DescribeProcess(title = "DigMap", description = "Digital map excerpt")
public class DigMapProcess  implements GSProcess {

        private Catalog catalog;
        private WMS wms;
        private ResourceLoader resourceLoader;

        public DigMapProcess(Catalog catalog, WMS wms, ResourceLoader resourceLoader) {
                this.catalog = catalog;
                this.wms = wms;
                this.resourceLoader = resourceLoader;
        }

        @DescribeResult(name = "digmap", description = "Return DigMap filename")
        public String execute(
                        @DescribeParameter(name = "userName", description = "Your name")
                        String userName,
                        @DescribeParameter(name = "countryName", description = "Country name")
                        String  countryName
                        ) throws ProcessException, Exception{
                //prepare CQL filter
                String cqlFilterString = "name like '" + countryName+ "'";
                //generate report and return report filename
                return  DigMapGenerator.generateDigMap(catalog, wms, resourceLoader, cqlFilterString, userName);
        }

}

The most of the processing is done in DigMapGenerator, so let’s take a look how it looks like:

package hr.yottabyte.digmap;

import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperRunManager;
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource;
import net.sf.jasperreports.engine.util.JRLoader;

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.wms.DefaultWebMapService;
import org.geoserver.wms.GetMap;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.MapLayerInfo;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WebMap;
import org.geoserver.wms.map.PNGMapResponse;
import org.geoserver.wms.map.RenderedImageMap;
import org.geotools.data.DataAccess;
import org.geotools.data.FeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.filter.text.cql2.CQL;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.geometry.BoundingBox;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import com.vividsolutions.jts.geom.Envelope;

public class DigMapGenerator {

        public static String generateDigMap(Catalog catalog, WMS wms, ResourceLoader resourceLoader, String cqlFilterString, String userName) throws Exception
        {
                String countryName = null;
                Name fName=null;;
                BoundingBox bb=null;
                JRMapCollectionDataSource dataSource=null;
                //create CQL filter based on input string
                Filter cqlFilter = CQL.toFilter(cqlFilterString);
                //open datastore
                DataStoreInfo  dsinfo = catalog.getDataStoreByName("opengeo","cntry_shp");
                DataAccess da = dsinfo.getDataStore(null);
                //get feature name
                fName = (Name) da.getNames().get(0);
                //grab feature source
                FeatureSource fs = da.getFeatureSource(fName);
                //apply filter on features
                FeatureCollection fc = fs.getFeatures(cqlFilter);
                //check do we have any feature in collection
                if (!fc.features().hasNext()) return "Please select your Country!";
                //cast feature to simple feature
                SimpleFeature feature = (SimpleFeature)fc.features().next();
                //get feature bbox
                bb = feature.getBounds();
                //prepare datasource for Jasper report - grab data from feature
                List<Map<String,?>> maps = new ArrayList<Map<String, ?>> ();
                Map<String,Object> map = new HashMap<String, Object>();
                countryName=feature.getAttribute("name_long").toString();
                map.put("opengeo:name_long",countryName);
                map.put("opengeo:postal",feature.getAttribute("postal").toString());
                map.put("opengeo:pop_est",feature.getAttribute("pop_est").toString());
                map.put("opengeo:continent",feature.getAttribute("continent").toString());
                map.put("gml:lowerCorner",bb.getMinY()+" "+bb.getMinX());
                map.put("gml:upperCorner",bb.getMaxY()+" "+bb.getMaxX());
                maps.add(map);
                //create new JasperReport source
                dataSource = new JRMapCollectionDataSource(maps);
                //get layer
                LayerInfo layer = catalog.getLayerByName("countries" );
                MapLayerInfo mli = new MapLayerInfo(layer);
                List <MapLayerInfo> mlil = new ArrayList<MapLayerInfo>();
                mlil.add(mli);
                //apply CQL filter to layer also
                List<Filter> filterList = new ArrayList<Filter>();
                filterList.add(cqlFilter);
                GetMapRequest request = new GetMapRequest();
                request.setFilter(filterList);
                request.setTransparent(true);
                request.setLayers(mlil);
                //set request envelope
                Envelope e = new Envelope(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY());
                request.setBbox(e);
                //create new wms
                DefaultWebMapService d = new DefaultWebMapService(wms);
                //better create new GetMap and set it to wms or except exceptions
                GetMap gm = new GetMap(wms);
                d.setGetMap(gm);
                //we'll take the shortcut and use reflect
                WebMap wm = d.reflect(request);
                //some more steps
                RenderedImageMap rim = (RenderedImageMap) wm;
                RenderedImage ri = rim.getImage();
                //and finaly the image
                PNGMapResponse image = new PNGMapResponse(wms);
                //some steps to return image as InputStream for jasper report
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                image.formatImageOutputStream(ri, os, rim.getMapContext());
                InputStream wmsInputStream = new ByteArrayInputStream(((ByteArrayOutputStream) os).toByteArray());
                //reference compiled report file
                Resource resource = resourceLoader.getResource("classpath:PersonalizedHelloWorld4WPS.jasper");
                InputStream jrIS = resource.getInputStream();
                JasperReport jasperReport = (JasperReport) JRLoader.loadObject(jrIS);
                //set report params
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("userName",userName);
                params.put("countryName",countryName);
                params.put("wmsInputStream",wmsInputStream);
                //create report
                byte[] pdfBytes = JasperRunManager.runReportToPdf(jasperReport,params, dataSource);
                String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
                String fileName=date+"-DigMap.pdf";
                String folderPath="/var/www/html/digmap/";
                //write report to file system
                Files.write(Paths.get(folderPath+fileName), pdfBytes);
                //return report path
                String serverURL= "http://digmap-dme.yottabyte.hr/";
                return serverURL+fileName;
        }
}

For the sake of simplicity let’s add additional DigMap wrapper. It print out country report referenced by point.

package hr.yottabyte.digmap;

import org.geoserver.catalog.Catalog;
import org.geoserver.wms.WMS;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.geotools.process.gs.GSProcess;
import org.springframework.core.io.ResourceLoader;

import com.vividsolutions.jts.geom.Geometry;

@DescribeProcess(title = "DigMapSimple", description = "Digital map excerpt")
public class DigMapSimpleProcess   implements GSProcess {

        private Catalog catalog;
        private WMS wms;
        private ResourceLoader resourceLoader;

        public DigMapSimpleProcess(Catalog catalog, WMS wms, ResourceLoader resourceLoader) {
                this.catalog = catalog;
                this.wms = wms;
                this.resourceLoader = resourceLoader;
        }

        @DescribeResult(name = "digmap", description = "Return DigMap filename")
        public String execute(
                        @DescribeParameter(name = "geometry", description = "Point")
                        Geometry geom
                        ) throws ProcessException, Exception{

                String cqlFilterString = "CONTAINS(the_geom,"+geom.toString()+")";
                return DigMapGenerator.generateDigMap(catalog, wms, resourceLoader, cqlFilterString, "");

        }

}

Before we can run this code we have to enable WPS processing on GeoServer (simply download WPS extension and unzip it to WEB-INF/lib folder). Also add jasperreports .jar file. Restart geoserver and let’s rock! Go to Geoserver UI, and select Demos. Create WPS process gs:DigMap, enter your username and country. Voila, copy file path and open it!

Download whole eclipse project in .zip  digmap.zip

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s