sascha@2502: package de.intevation.flys.client.server; sascha@2502: sascha@2504: import de.intevation.artifacts.common.ArtifactNamespaceContext; sascha@2504: import de.intevation.artifacts.common.utils.ClientProtocolUtils; sascha@2504: import de.intevation.artifacts.common.utils.JSON; sascha@2504: import de.intevation.artifacts.common.utils.StringUtils; sascha@2504: import de.intevation.artifacts.common.utils.XMLUtils; sascha@2504: import de.intevation.artifacts.httpclient.exceptions.ConnectionException; sascha@2504: import de.intevation.artifacts.httpclient.http.HttpClient; sascha@2504: import de.intevation.artifacts.httpclient.http.HttpClientImpl; sascha@2506: import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler; felix@4436: import de.intevation.flys.client.shared.MapUtils; felix@4436: import de.intevation.flys.client.shared.model.MapConfig; felix@4433: felix@4436: import java.io.IOException; felix@4436: import java.io.InputStream; felix@4436: import java.io.OutputStream; felix@4436: import java.io.UnsupportedEncodingException; felix@4436: import java.net.URLEncoder; felix@4436: import java.util.ArrayList; felix@4436: import java.util.Collections; christian@4474: import java.util.Enumeration; christian@4473: import java.util.HashMap; felix@4436: import java.util.LinkedHashMap; felix@4436: import java.util.List; felix@4436: import java.util.Map; sascha@2506: felix@4436: import javax.servlet.ServletException; felix@4436: import javax.servlet.http.HttpServlet; felix@4436: import javax.servlet.http.HttpServletRequest; felix@4436: import javax.servlet.http.HttpServletResponse; felix@4436: felix@4436: import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; felix@4436: import org.apache.commons.httpclient.methods.GetMethod; felix@4436: import org.apache.log4j.Logger; felix@4436: import org.w3c.dom.Document; felix@4436: import org.w3c.dom.Element; felix@4436: import org.w3c.dom.NodeList; felix@4433: /* felix@4433: import java.io.BufferedInputStream; felix@4433: import java.io.BufferedOutputStream; felix@4433: import java.io.File; felix@4433: import java.io.FileInputStream; felix@4433: import java.io.FileOutputStream; felix@4433: */ sascha@2519: /* Used by direct API call. -> Enforce GPLv3 sascha@2502: import org.mapfish.print.MapPrinter; sascha@2502: import org.mapfish.print.output.OutputFactory; sascha@2502: import org.mapfish.print.output.OutputFormat; sascha@2502: sascha@2502: import org.mapfish.print.utils.PJsonObject; sascha@2905: */ sascha@2502: sascha@2502: public class MapPrintServiceImpl sascha@2502: extends HttpServlet sascha@2502: { sascha@2502: private static final Logger log = sascha@2502: Logger.getLogger(MapPrintServiceImpl.class); sascha@2502: sascha@2506: protected static class Layer implements Comparable { sascha@2504: sascha@2506: protected int pos; sascha@2506: protected String url; sascha@2506: protected String layers; sascha@2506: protected String description; sascha@2504: sascha@2506: public Layer() { sascha@2506: } sascha@2504: sascha@2506: public boolean setup(Element element) { sascha@2504: sascha@2506: Element parent = (Element)element.getParentNode(); sascha@2506: String parentName = parent.getAttribute("name"); sascha@2905: if (!(parentName.equals("map") sascha@2506: || parentName.equals("floodmap"))) { sascha@2506: return false; sascha@2504: } sascha@2506: sascha@2506: String ns = ArtifactNamespaceContext.NAMESPACE_URI; sascha@2506: sascha@2506: String visible = element.getAttributeNS(ns, "visible"); sascha@2506: String active = element.getAttributeNS(ns, "active"); sascha@2506: sascha@2506: if (visible.equals("0") || active.equals("0")) { sascha@2506: return false; sascha@2506: } sascha@2506: sascha@2506: url = element.getAttributeNS(ns, "url"); sascha@2506: layers = element.getAttributeNS(ns, "layers"); sascha@2506: description = element.getAttributeNS(ns, "description"); sascha@2506: sascha@2506: try { sascha@2506: pos = Integer.parseInt(element.getAttributeNS(ns, "pos")); sascha@2506: } sascha@2506: catch (NumberFormatException nfe) { sascha@2506: return false; sascha@2506: } sascha@2506: sascha@2506: return true; sascha@2506: } sascha@2506: sascha@2506: public Map toMap() { sascha@2504: Map layer = new LinkedHashMap(); sascha@2504: sascha@2504: layer.put("type", "WMS"); sascha@2504: List subLayers = new ArrayList(1); sascha@2506: subLayers.add(layers); sascha@2504: layer.put("layers", subLayers); aheinecke@5676: // XXX: osm.intevation.de mapache only offers low dpi maps aheinecke@5676: // so we need to use the uncached service aheinecke@5676: layer.put("baseURL", url.replace("http://osm.intevation.de/mapcache/?", aheinecke@5676: "http://osm.intevation.de/cgi-bin/germany.fcgi?")); sascha@2506: layer.put("format", "image/png"); // TODO: Make configurable. sascha@2504: sascha@2506: return layer; sascha@2506: } sascha@2506: sascha@2506: @Override sascha@2506: public int compareTo(Layer other) { sascha@2506: int d = pos - other.pos; sascha@2506: if (d < 0) return -1; sascha@2506: return d > 0 ? +1 : 0; sascha@2506: } sascha@2506: } // class Layer sascha@2506: sascha@2506: protected static String generateSpec( sascha@2506: Document descDocument, sascha@2508: MapConfig mapConfig, sascha@2508: Double minX, Double minY, christian@4473: Double maxX, Double maxY, christian@4474: Map pageSpecs sascha@2506: ) { sascha@2506: Map spec = new LinkedHashMap(); aheinecke@5676: int dpi = 254; christian@4969: spec.put("layout", "A4 landscape"); christian@4474: spec.put("pageSize", "A4"); aheinecke@5676: spec.put("landscape", "true"); sascha@2506: spec.put("srs", "EPSG:" + mapConfig.getSrid()); aheinecke@5676: spec.put("dpi", dpi); sascha@2508: spec.put("units", "m"); sascha@2510: spec.put("geodaetic", "true"); sascha@2506: spec.put("outputFormat", "pdf"); sascha@2506: christian@4474: spec.putAll(pageSpecs); christian@4474: sascha@2506: String ns = ArtifactNamespaceContext.NAMESPACE_URI; sascha@2506: sascha@2510: List ls = new ArrayList(); christian@4472: Layer l = new Layer(); sascha@2510: christian@4472: NodeList facets = descDocument.getElementsByTagNameNS(ns, "facet"); christian@4472: christian@4472: for (int i = 0, N = facets.getLength(); i < N; ++i) { christian@4472: Element element = (Element)facets.item(i); christian@4472: if (l.setup(element)) { christian@4472: ls.add(l); christian@4472: l = new Layer(); sascha@2506: } sascha@2506: } sascha@2506: sascha@2506: // Establish Z order. sascha@2506: Collections.sort(ls); sascha@2506: sascha@2506: List layers = new ArrayList(ls.size()); sascha@2506: sascha@2510: for (int i = ls.size()-1; i >= 0; --i) { sascha@2510: layers.add(ls.get(i).toMap()); sascha@2504: } sascha@2504: sascha@2504: spec.put("layers", layers); sascha@2504: spec.put("name", "Name"); sascha@2504: sascha@2504: List pages = new ArrayList(1); sascha@2504: sascha@2504: sascha@2504: Map page = new LinkedHashMap(); sascha@2504: sascha@2504: List bounds = new ArrayList(4); sascha@2510: bounds.add(minX); sascha@2510: bounds.add(minY); sascha@2510: bounds.add(maxX); sascha@2510: bounds.add(maxY); sascha@2510: page.put("bbox", bounds); sascha@2504: sascha@2510: /* sascha@2510: bounds.add(Double.valueOf((minX+maxX)*0.5)); sascha@2510: bounds.add(Double.valueOf((minY+maxY)*0.5)); sascha@2510: sascha@2510: page.put("center", bounds); sascha@2510: page.put("scale", Integer.valueOf(50000)); sascha@2510: */ sascha@2504: sascha@2504: page.put("rotation", Integer.valueOf(0)); sascha@2504: christian@4473: // This may overwrite default settings above christian@4474: page.putAll(pageSpecs); christian@4473: sascha@2504: pages.add(page); sascha@2510: spec.put("pages", pages); sascha@2510: sascha@2510: List legends = new ArrayList(layers.size()); sascha@2510: sascha@2510: for (Layer layer: ls) { sascha@2510: Map legend = new LinkedHashMap(); sascha@2510: List classes = new ArrayList(1); sascha@2510: Map clazz = new LinkedHashMap(); aheinecke@5676: String lgu = MapUtils.getLegendGraphicUrl(layer.url, layer.layers, dpi); sascha@2510: clazz.put("icon", lgu); sascha@2510: clazz.put("name", layer.description); sascha@2510: classes.add(clazz); sascha@2510: legend.put("classes", classes); sascha@2510: legend.put("name", layer.description); sascha@2510: legends.add(legend); sascha@2510: } sascha@2510: sascha@2510: spec.put("legends", legends); sascha@2504: sascha@2504: return JSON.toJSONString(spec); sascha@2504: } sascha@2502: sascha@2502: sascha@2503: @Override sascha@2905: public void doGet(HttpServletRequest req, HttpServletResponse resp) sascha@2503: throws ServletException, IOException sascha@2503: { sascha@2503: log.info("MapPrintServiceImpl.doGet"); sascha@2503: sascha@2503: String uuid = req.getParameter("uuid"); sascha@2503: sascha@2503: if (uuid == null || !StringUtils.checkUUID(uuid)) { sascha@2503: throw new ServletException("Missing or misspelled UUID"); sascha@2503: } sascha@2503: sascha@2503: String minXS = req.getParameter("minx"); sascha@2503: String maxXS = req.getParameter("maxx"); sascha@2503: String minYS = req.getParameter("miny"); sascha@2503: String maxYS = req.getParameter("maxy"); sascha@2503: sascha@2508: Double minX = null; sascha@2508: Double maxX = null; sascha@2508: Double minY = null; sascha@2508: Double maxY = null; sascha@2503: sascha@2508: if (minXS != null && maxXS != null sascha@2515: && minYS != null && maxYS != null) { sascha@2515: log.debug("all parameters found -> parsing"); sascha@2508: try { sascha@2508: minX = Double.parseDouble(minXS); sascha@2508: minY = Double.parseDouble(minYS); sascha@2508: maxX = Double.parseDouble(maxXS); sascha@2508: maxY = Double.parseDouble(maxYS); sascha@2508: } sascha@2508: catch (NumberFormatException nfe) { sascha@2508: throw new ServletException("Misspelled minX, minY, maxX or maxY"); sascha@2508: } sascha@2503: } sascha@2503: sascha@2503: String mapType = req.getParameter("maptype"); sascha@2503: sascha@2508: if (mapType == null || !mapType.equals("floodmap")) { sascha@2508: mapType = "map"; sascha@2503: } sascha@2503: christian@4473: // Retrieve print settings from request christian@4474: Map pageSpecs = new HashMap(); aheinecke@5622: Map data = new HashMap(); aheinecke@5622: List payload = new ArrayList(); aheinecke@5622: data.put("data", payload); aheinecke@5677: Enumeration paramNames = req.getParameterNames(); aheinecke@5677: List params = Collections.list(paramNames); aheinecke@5677: Collections.sort(params); aheinecke@5677: for (String paramName : params) { aheinecke@5677: if (paramName.startsWith("mapfish_data_")) { aheinecke@5677: // You can add mapfish_data variables that will be mapped aheinecke@5622: // to a info value pairs to provide meta data for the map aheinecke@5677: // The the info part starts with a number for sorting that aheinecke@5677: // number will be stripped aheinecke@5622: String paramValue = req.getParameter(paramName); aheinecke@5677: Map data3 = new HashMap(); aheinecke@5677: int order = 0; aheinecke@5677: try { aheinecke@5677: order = Integer.parseInt(paramName.substring(13, 14)); aheinecke@5677: data3.put("info", paramName.substring(14)); aheinecke@5677: } catch (NumberFormatException nfe) { aheinecke@5622: data3.put("info", paramName.substring(13)); aheinecke@5622: payload.add(data3); aheinecke@5622: } aheinecke@5677: if (paramValue.equals("null")) aheinecke@5677: data3.put("value", ""); aheinecke@5677: else aheinecke@5677: data3.put("value", paramValue); aheinecke@5677: payload.add(data3); aheinecke@5677: } else if (paramName.startsWith("mapfish_")) { christian@4474: String paramValue = req.getParameter(paramName); aheinecke@5687: if (paramValue.equals("null")) aheinecke@5677: paramValue = ""; christian@4474: pageSpecs.put(paramName.substring(8), paramValue); christian@4474: } christian@4474: } aheinecke@5622: if (!payload.isEmpty()) { aheinecke@5622: pageSpecs.put("data", data); aheinecke@5622: List columns = new ArrayList(); aheinecke@5622: columns.add("info"); aheinecke@5622: columns.add("value"); aheinecke@5622: data.put("columns", columns); aheinecke@5622: } christian@4473: sascha@2503: String url = getURL(); sascha@2503: sascha@2506: Document requestOut = sascha@2506: ClientProtocolUtils.newOutCollectionDocument( sascha@2506: uuid, mapType, mapType); sascha@2506: Document requestDesc = sascha@2506: ClientProtocolUtils.newDescribeCollectionDocument(uuid); sascha@2503: sascha@2506: Document outDocument; sascha@2506: Document descDocument; sascha@2506: sascha@2503: try { sascha@2503: HttpClient client = new HttpClientImpl(url); sascha@2506: sascha@2508: descDocument = (Document)client.doCollectionAction( sascha@2508: requestDesc, uuid, new DocumentResponseHandler()); sascha@2508: sascha@2506: InputStream is = client.collectionOut( sascha@2506: requestOut, uuid, mapType); sascha@2506: sascha@2503: try { sascha@2506: outDocument = XMLUtils.parseDocument(is); sascha@2503: } sascha@2503: finally { sascha@2503: is.close(); sascha@2506: is = null; sascha@2503: } sascha@2506: sascha@2503: } sascha@2503: catch (ConnectionException ce) { sascha@2503: log.error(ce); sascha@2503: throw new ServletException(ce); sascha@2503: } sascha@2503: sascha@2508: MapConfig mapConfig = MapHelper.parseConfig(outDocument); sascha@2508: sascha@2508: if (minX == null) { sascha@2515: log.debug("parameters missing -> fallback to max extent"); sascha@2508: String [] parts = mapConfig.getMaxExtent().split("\\s+"); sascha@2508: if (parts.length < 4) { sascha@2508: throw new ServletException( sascha@2508: "Max extent has less than 4 values"); sascha@2508: } sascha@2508: try { sascha@2508: minX = Double.valueOf(parts[0]); sascha@2508: minY = Double.valueOf(parts[1]); sascha@2508: maxX = Double.valueOf(parts[2]); sascha@2508: maxY = Double.valueOf(parts[3]); sascha@2508: } sascha@2508: catch (NumberFormatException nfe) { sascha@2508: throw new ServletException(nfe); sascha@2508: } sascha@2508: } sascha@2510: if (log.isDebugEnabled()) { sascha@2510: log.debug("minX: " + minX); sascha@2510: log.debug("maxX: " + maxX); sascha@2510: log.debug("minY: " + minY); sascha@2510: log.debug("maxY: " + maxY); sascha@2510: } sascha@2508: sascha@2504: String spec = generateSpec( sascha@2506: descDocument, sascha@2508: mapConfig, sascha@2504: minX, minY, christian@4473: maxX, maxY, christian@4474: pageSpecs); sascha@2503: sascha@2504: if (log.isDebugEnabled()) { sascha@2510: log.debug("Generated spec:"); sascha@2504: log.debug(spec); sascha@2504: //System.err.println(spec); sascha@2504: } sascha@2503: sascha@2504: producePDF(spec, resp); sascha@2503: } sascha@2503: sascha@2503: protected String getURL() throws ServletException { sascha@2503: String url = getServletContext().getInitParameter("server-url"); sascha@2503: if (url == null) { sascha@2503: throw new ServletException("Missing server-url"); sascha@2503: } sascha@2503: return url; sascha@2503: } sascha@2503: sascha@2519: private static final String encode(String s) { sascha@2519: try { sascha@2519: return URLEncoder.encode(s, "UTF-8"); sascha@2519: } sascha@2519: catch (UnsupportedEncodingException usee) { sascha@2519: // Should not happen. sascha@2519: return s; sascha@2519: } sascha@2519: } sascha@2519: sascha@2519: protected void producePDF(String json, HttpServletResponse resp) sascha@2519: throws ServletException, IOException sascha@2519: { sascha@2519: String printUrl = getInitParameter("print-url"); sascha@2519: sascha@2519: if (printUrl == null) { sascha@2519: throw new ServletException("Missing 'print-url' in web.xml"); sascha@2519: } sascha@2519: sascha@2519: String url = printUrl + "/print.pdf?spec=" + encode(json); sascha@2519: sascha@2519: org.apache.commons.httpclient.HttpClient client = sascha@2519: new org.apache.commons.httpclient.HttpClient( sascha@2519: new MultiThreadedHttpConnectionManager()); sascha@2519: felix@4436: // FIXME: The request is not authenticated. felix@4436: // Currently this is not a problem because /flys/map-print felix@4436: // is whitelisted in GGInAFilter. sascha@2519: GetMethod get = new GetMethod(url); sascha@2519: int result = client.executeMethod(get); sascha@2519: InputStream in = get.getResponseBodyAsStream(); sascha@2519: sascha@2519: if (in != null) { sascha@2519: try { sascha@2519: OutputStream out = resp.getOutputStream(); sascha@2519: try { sascha@2519: byte [] buf = new byte[4096]; sascha@2519: int r; aheinecke@5720: boolean checkedErrors = false; sascha@2519: while ((r = in.read(buf)) >= 0) { aheinecke@5720: if (!checkedErrors) { aheinecke@5720: checkedErrors = true; aheinecke@5720: String header = new String(buf, "UTF-8"); aheinecke@5720: if (header.contains("Error while generating PDF:")) { aheinecke@5720: resp.setContentType("text/plain"); aheinecke@5720: } else { aheinecke@5720: // Only send content disposition and filename content aheinecke@5720: // type when we have a pdf aheinecke@5720: resp.setHeader("Content-Disposition", aheinecke@5720: "attachment;filename=flys-karte.pdf"); aheinecke@5720: resp.setContentType("application/pdf"); aheinecke@5720: } aheinecke@5720: aheinecke@5720: } sascha@2519: out.write(buf, 0, r); sascha@2519: } sascha@2519: out.flush(); sascha@2519: } sascha@2519: finally { sascha@2519: out.close(); sascha@2519: } sascha@2519: } sascha@2519: finally { sascha@2519: in.close(); sascha@2519: } sascha@2519: } sascha@2519: } sascha@2519: sascha@2519: /* Use this if you want directly call the MapPrinter. Enforces GPLv3! sascha@2519: sascha@2503: protected MapPrinter getMapPrinter() throws ServletException, IOException { sascha@2502: String configPath = getInitParameter("config"); sascha@2502: if (configPath == null) { sascha@2502: throw new ServletException("Missing configuration in web.xml"); sascha@2502: } sascha@2502: sascha@2502: File configFile = new File(configPath); sascha@2502: if (!configFile.isAbsolute()) { sascha@2502: configFile = new File(getServletContext().getRealPath(configPath)); sascha@2502: } sascha@2502: sascha@2502: if (!configFile.isFile() || !configFile.canRead()) { sascha@2502: throw new ServletException("Cannot read '" + configFile + "'"); sascha@2502: } sascha@2502: sascha@2502: return new MapPrinter(configFile); sascha@2502: } sascha@2502: sascha@2503: protected void producePDF(String json, HttpServletResponse resp) sascha@2503: throws ServletException, IOException sascha@2502: { sascha@2504: PJsonObject jsonSpec = MapPrinter.parseSpec(json); sascha@2502: sascha@2502: MapPrinter printer = getMapPrinter(); sascha@2502: sascha@2502: OutputFormat outputFormat = OutputFactory.create( sascha@2502: printer.getConfig(), jsonSpec); sascha@2502: sascha@2502: resp.setHeader("Content-Disposition", "attachment;filename=print.pdf"); sascha@2502: resp.setHeader("Content-Type", "application/pdf"); sascha@2502: sascha@2502: // XXX: Streaming the generated PDF directly sascha@2502: // to the request out does not work. :-/ sascha@2502: File tmpFile = File.createTempFile("map-printing", null); sascha@2502: sascha@2502: try { sascha@2502: OutputStream out = sascha@2502: new BufferedOutputStream( sascha@2502: new FileOutputStream(tmpFile)); sascha@2502: try { sascha@2502: outputFormat.print(printer, jsonSpec, out, ""); sascha@2502: out.flush(); sascha@2502: } sascha@2502: catch (Exception e) { sascha@2502: log.error(e); sascha@2502: throw new ServletException(e); sascha@2502: } sascha@2502: finally { sascha@2502: printer.stop(); sascha@2502: out.close(); sascha@2502: } sascha@2502: InputStream in = sascha@2502: new BufferedInputStream( sascha@2502: new FileInputStream(tmpFile)); sascha@2502: out = resp.getOutputStream(); sascha@2502: try { sascha@2502: byte [] buf = new byte[4096]; sascha@2502: int r; sascha@2502: while ((r = in.read(buf)) >= 0) { sascha@2502: out.write(buf, 0, r); sascha@2502: } sascha@2502: out.flush(); sascha@2502: } sascha@2502: finally { sascha@2502: in.close(); sascha@2502: out.close(); sascha@2502: } sascha@2502: } sascha@2502: finally { sascha@2502: if (tmpFile.exists()) { sascha@2502: tmpFile.delete(); sascha@2502: } sascha@2502: } sascha@2502: } sascha@2519: */ sascha@2502: } sascha@2502: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :