/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs.response;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.geoserver.config.GeoServer;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
import org.geoserver.wfs.WFSGetFeatureOutputFormat;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.request.GetFeatureRequest;
import org.geoserver.wfs.request.Query;
import org.geoserver.wfs.response.dxf.DXFWriter;
import org.geoserver.wfs.response.dxf.DXFWriterFinder;
import org.geoserver.wfs.response.dxf.LineType;
import org.geotools.util.logging.Logging;
/**
* This class returns a dxf encoded results of the users's query
* (optionally zipped).
* Several format options are available to control output generation.
* - version: (number) creates a DXF in the specified version format (a DXFWriter
* implementation supporting the requested version needs to be available
* or an exception will be thrown); the default implementation creates
* a version 14 DXF.
* - asblock: (true/false) if true, all geometries are written
* as blocks and then inserted as entities. If false, simple geometries
* are directly written as entities.
* - colors: (comma delimited list of numbers): colors to be used for
* the DXF layers, in sequence. If layers are more than the specified
* colors, they will be reused many times. A set of default colors is
* used if the option is not used. Colors are AutoCad color numbers
* (7=white, etc.).
* - ltypes: (comma delimited list of line type descriptors): line types
* to be used for the DXF layers, in sequence. If layers are more than
* the specified line types, they will be reused many times. If not specified,
* all layers will be given a solid, continuous line type. A descriptor
* has the following format: <name>!<repeatable pattern>[!<base length>], where
* <name> is the name assigned to the line type, <base length> (optional)
* is a real number that tells how long is each part of the line pattern
* (defaults to 0.125), and <repeatable pattern> is a visual description
* of the repeatable part of the line pattern, as a sequence of - (solid line),
* * (dot) and _ (empty space).
* - layers: (comma delimited list of strings) names to be assigned to
* the DXF layers. If specified, must contain a name for each requested
* query. By default a standard name will be assigned to layers.
*
* A different layer will be generated for each requested query.
* Layer names can be chosen using the layers format option, or
* in POST mode, using the handle attribute of the Query tag.
* The name of the resulting file can be chosen using the handle
* attribute of the GetFeature tag. By default, the names of layers
* concatenated with _ will be used.
*
* @author Mauro Bartolomeoli, mbarto@infosia.it
*
*/
public class DXFOutputFormat extends WFSGetFeatureOutputFormat {
private static final Logger LOGGER = Logging.getLogger(DXFOutputFormat.class);
public static final Set<String> formats = new HashSet<String>();
static {
// list of supported output formats
formats.add("DXF");
formats.add("DXF-ZIP");
}
public DXFOutputFormat(GeoServer gs) {
super(gs,formats);
}
/**
* Gets current request extension (dxf or zip).
*
* @param operation
*
*/
public String getDxfExtension(Operation operation) {
GetFeatureRequest request = GetFeatureRequest.adapt(operation
.getParameters()[0]);
String outputFormat = request.getOutputFormat().toUpperCase();
// DXF
if (outputFormat.equals("DXF"))
return "dxf";
// DXF-ZIP
return "zip";
}
/**
* Mime type: application/dxf or application/zip
*/
@Override
public String getMimeType(Object value, Operation operation) throws ServiceException {
return "application/" + getDxfExtension(operation);
}
@Override
public String getPreferredDisposition(Object value, Operation operation) {
return DISPOSITION_ATTACH;
}
/**
* Gets output filename.
* If the handle attribute is defined on the GetFeature tag it
* will be used, else the name is obtained concatenating layer names
* with underscore as a separator (up to a maximum name length).
*/
private String getFileName(Operation operation) {
GetFeatureRequest request = GetFeatureRequest.adapt(operation
.getParameters()[0]);
if (request.getHandle() != null) {
LOGGER.log(Level.FINE,
"Using handle for file name: " + request.getHandle());
return request.getHandle();
}
List<String> names = new ArrayList<String>();
for (Query query : request.getQueries()) {
addLayerNames(names, query, false);
}
String layerNames = StringUtils.join(names.toArray(), '_');
LOGGER.log(Level.FINE, "Using layer names for file name: " + layerNames);
if (layerNames.length() > 20) {
LOGGER.log(Level.WARNING,
"Calculated filename too long. Returing a shorter one: "
+ layerNames.substring(0, 20));
return layerNames.substring(0, 20);
}
return layerNames;
}
private void addLayerNames(List<String> names, Query query, boolean toUpper) {
for (QName name : query.getTypeNames()) {
String localName = name.getLocalPart();
if (toUpper) {
localName = localName.toUpperCase();
}
names.add(localName);
}
}
@Override
public String getAttachmentFileName(Object value, Operation operation) {
return getFileName(operation) + '.' + getDxfExtension(operation);
}
/**
* Actually write the given featurecollection as a dxf file to
* the output stream.
*
* @see org.geoserver.wfs.WFSGetFeatureOutputFormat#write(net.opengis.wfs.FeatureCollectionType,
* java.io.OutputStream, org.geoserver.platform.Operation)
*/
@Override
protected void write(FeatureCollectionResponse featureCollection, OutputStream output,
Operation operation) throws IOException, ServiceException {
// output format (zipped or not)
String format = getDxfExtension(operation);
BufferedWriter w = null;
ZipOutputStream zipStream = null;
// DXF: use a simple buffered writer
if (format.equals("dxf")) {
LOGGER.log(Level.FINE,"Plain DXF output");
w = new BufferedWriter(new OutputStreamWriter(output));
} else {
LOGGER.log(Level.FINE,"Zipped DXF output");
// DXF-ZIP: use a zip stream wrapped with the buffered writer
zipStream = new ZipOutputStream(output);
ZipEntry entry = new ZipEntry(getFileName(operation) + ".dxf");
zipStream.putNextEntry(entry);
w = new BufferedWriter(new OutputStreamWriter(zipStream));
}
// extract format_options (GET mode)
GetFeatureRequest gft = GetFeatureRequest.adapt(operation.getParameters()[0]);
String version = (String) gft.getFormatOptions().get("VERSION");
String blocks = (String) gft.getFormatOptions().get("ASBLOCKS");
String colors = (String) gft.getFormatOptions().get("COLORS");
String ltypes = (String) gft.getFormatOptions().get("LTYPES");
String[] layers = null;
if(gft.getFormatOptions().get("LAYERS") instanceof String) {
layers = ((String) gft.getFormatOptions().get("LAYERS")).split(",");
} else if(gft.getFormatOptions().get("LAYERS") instanceof List) {
layers = (String[]) ((List) gft.getFormatOptions().get("LAYERS")).toArray(new String[0]);
}
if(layers != null) {
for(int count = 0; count < layers.length; count++) {
layers[count] = layers[count].toUpperCase();
}
}
String writeAttributes = (String) gft.getFormatOptions().get("WITHATTRIBUTES");
LOGGER.log(Level.FINE,"Format options: "+version+"; "+blocks+"; "+colors+"; "+ltypes+"; "+StringUtils.join(layers, ",")+"; "+writeAttributes);
// get a suitable DXFWriter, for the requested version (null -> get any writer)
DXFWriter dxfWriter = DXFWriterFinder.getWriter(version, w);
if (dxfWriter != null) {
LOGGER.log(Level.INFO,"DXFWriter: "+dxfWriter.getDescription());
if(layers == null) {
layers=getLayerNames(gft.getQueries());
}
LOGGER.log(Level.FINE,"Layers names: "+StringUtils.join(layers,","));
dxfWriter.setOption("layers", layers);
if(writeAttributes != null) {
dxfWriter.setOption("writeattributes", writeAttributes.toLowerCase().equals("true"));
}
if (blocks != null && blocks.toLowerCase().equals("true"))
dxfWriter.setOption("geometryasblock", true);
// set optional colors
if (colors != null) {
try {
String[] sColors = colors.split(",");
int[] icolors = new int[sColors.length];
for (int count = 0; count < sColors.length; count++)
icolors[count] = Integer.parseInt(sColors[count]);
dxfWriter.setOption("colors", icolors);
} catch (Throwable t) {
LOGGER.log(Level.WARNING,"format option colors ignored by DXFOutputFormat due to a wrong format: "+t.getMessage());
}
}
// set optional line types
if (ltypes != null) {
try {
String[] sLTypes = ltypes.split(",");
LineType[] ltypesArr = new LineType[sLTypes.length];
for (int count = 0; count < sLTypes.length; count++)
ltypesArr[count] = LineType.parse(sLTypes[count]);
dxfWriter.setOption("linetypes", ltypesArr);
} catch (Throwable t) {
LOGGER
.warning("format option ltypes ignored by DXFOutputFormat due to a wrong format: "+t.getMessage());
}
}
// do the real job, please
dxfWriter.write(featureCollection.getFeature(),version);
w.flush();
if (zipStream != null) {
zipStream.closeEntry();
zipStream.close();
}
dxfWriter = null;
zipStream = null;
w = null;
} else
throw new UnsupportedOperationException("Version " + version
+ " not supported by dxf output format");
}
/**
* Gets a list of names for layers, one
* for each query.
* @param it
*
*/
private String[] getLayerNames(List<Query> queries) {
List<String> names = new ArrayList<String>();
for (Query query : queries) {
addLayerNames(names, query, true);
}
return names.toArray(new String[] {});
}
}