/* Copyright 2014 The jeo project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.jeo.json; import com.vividsolutions.jts.geom.Envelope; import io.jeo.data.Dataset; import io.jeo.data.Driver; import io.jeo.data.Handle; import io.jeo.data.Workspace; import io.jeo.geom.Bounds; import io.jeo.raster.Band; import io.jeo.raster.RasterDataset; import io.jeo.tile.TileDataset; import io.jeo.tile.TileGrid; import io.jeo.tile.TilePyramid; import io.jeo.util.Key; import io.jeo.util.Messages; import io.jeo.vector.Field; import io.jeo.geojson.GeoJSONWriter; import io.jeo.util.Dimension; import io.jeo.vector.VectorQuery; import io.jeo.vector.VectorDataset; import org.osgeo.proj4j.CoordinateReferenceSystem; import java.io.IOException; import java.io.Writer; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.String.format; /** * Writes common jeo objects as JSON. * <p> * Example: * <pre><code> * Writer w = ...; * Dataset data = ...; * * JeoJSONWriter writer = new JeoJSONWriter(w); * writer.object() * writer.key("data").dataset(data) * writer.endObject(); * </code></pre> * </p> * @author Justin Deoliveira, Boundless */ public class JeoJSONWriter extends GeoJSONWriter { /** * Creates a new writer. * * @param out The writer to encode to. */ public JeoJSONWriter(Writer out) { super(out); } /** * Creates a new writer with formatting. * * @param out The writer to encode to. * @param indentSize The number of spaces to use when indenting. */ public JeoJSONWriter(Writer out, int indentSize) { super(out, indentSize); } /** * Encodes a driver object. */ public JeoJSONWriter driver(Driver<?> drv, Map<String,Object> extra) throws IOException { object(); key("name").value(drv.name()); Messages msgs = new Messages(); key("enabled").value(drv.isEnabled(msgs)); key("aliases").array(); for (String s : drv.aliases()) { value(s); } endArray(); List<Throwable> errors = msgs.list(); if (!errors.isEmpty()) { key("messages").array(); for (Throwable t : errors) { value(format(Locale.ROOT, "%s: %s", t.getClass().getName(), t.getMessage())); } endArray(); } key("type"); Class<?> type = drv.type(); if (Workspace.class.isAssignableFrom(type)) { value("workspace"); } else if (Dataset.class.isAssignableFrom(type)) { value("dataset"); } else { value(drv.type().getSimpleName()); } key("keys").object(); for (Key<?> key : drv.keys()) { key(key.name()).object(); key("type").value(key.type().getSimpleName()); if (key.def() != null) { key("default").value(key.def()); } endObject(); } endObject(); // keys encode(extra); endObject(); // root return this; } /** * Encodes a workspace object. */ public JeoJSONWriter workspace(Workspace ws) throws IOException { object(); key("type").value("workspace"); key("driver").value(ws.driver().name()); key("datasets").array(); for (Handle<Dataset> ref : ws.list()) { value(ref.name()); } endArray(); return endObject(); } /** * Encodes a Dataset object. */ public JeoJSONWriter dataset(Dataset ds) throws IOException { if (ds instanceof VectorDataset) { dataset((VectorDataset)ds); } else if (ds instanceof RasterDataset) { dataset((RasterDataset)ds); } else if (ds instanceof TileDataset) { dataset((TileDataset)ds); } else { object().encode(ds).endObject(); } return this; } /** * Encodes a VectorDataset object. */ public JeoJSONWriter dataset(VectorDataset vds) throws IOException { return dataset(vds, null); } /** * Encodes a VectorDataset object. * <p> * The <tt>extra</tt> parameter can be used to encode extra attributes. It may be * <tt>null</tt>. * </p> */ public JeoJSONWriter dataset(VectorDataset vds, Map<String,Object> extra) throws IOException { object(); encode(vds); key("count").value(vds.count(new VectorQuery())); key("schema").object(); for (Field fld : vds.schema()) { key(fld.name()).value(fld.type().getSimpleName()); } endObject(); encode(extra); return endObject(); } /** * Encodes a RasterDataset object. */ public JeoJSONWriter dataset(RasterDataset rds) throws IOException { return dataset(rds, null); } /** * Encodes a RasterDataset object. * <p> * The <tt>extra</tt> parameter can be used to encode extra attributes. It may be * <tt>null</tt>. * </p> */ public JeoJSONWriter dataset(RasterDataset rds, Map<String,Object> extra) throws IOException { object(); encode(rds); Dimension size = rds.size(); key("size").array().value(size.width()).value(size.height()).endArray(); key("bands").array(); for (Band band : rds.bands()) { object(); key("name").value(band.name()); key("type").value(band.datatype()); key("color").value(band.color()); if (band.nodata() != null) { key("nodata").value(band.nodata()); } endObject(); } endArray(); encode(extra); return endObject(); } /** * Encodes a TileDataset object. */ public JeoJSONWriter dataset(TileDataset tds) throws IOException { return dataset(tds, null); } /** * Encodes a TileDataset object. * <p> * The <tt>extra</tt> parameter can be used to encode extra attributes. It may be * <tt>null</tt>. * </p> */ public JeoJSONWriter dataset(TileDataset tds, Map<String,Object> extra) throws IOException { object(); encode(tds); TilePyramid pyr = tds.pyramid(); key("tilesize").array().value(pyr.tileWidth()) .value(pyr.tileHeight()).endArray(); key("grids").array(); for (TileGrid grid : tds.pyramid().grids()) { object().key("zoom").value(grid.z()).key("width") .value(grid.width()).key("height") .value(grid.height()).key("res").array() .value(grid.xres()).value(grid.yres()).endArray() .endObject(); } endArray(); encode(extra); return endObject(); } /** * Helper to encode common Dataset attributes. */ JeoJSONWriter encode(Dataset ds) throws IOException { key("name").value(ds.name()); key("type"); if (ds instanceof VectorDataset) { value("vector"); } else if (ds instanceof TileDataset ){ value("tile"); } else if (ds instanceof RasterDataset) { value("raster"); } else { nul(); } key("driver").value(ds.driver().name()); Envelope bbox = ds.bounds(); if (!Bounds.isNull(bbox)) { key("bbox"); bbox(bbox); } CoordinateReferenceSystem crs = ds.crs(); if (crs != null) { key("crs").proj(crs); } return this; } /** * Helper to encode a map of key value pairs recursively. */ JeoJSONWriter encode(Map<String,Object> map) throws IOException { if (map == null) { return this; } for (Map.Entry<String,Object> e : map.entrySet()) { key(e.getKey()); if (e.getValue() instanceof Map) { object(); encode((Map<String,Object>)e.getValue()); endObject(); } else { value(e.getValue()); } } return this; } static Pattern PROJ_PARAM_RE = Pattern.compile("([\\+-])?(\\w+)(?:=(\\w+))?"); public JeoJSONWriter proj(CoordinateReferenceSystem crs) throws IOException { object(); key("name").value(crs.getName()); if (crs.getParameters() != null) { key("params").object(); for (String s : crs.getParameters()) { Matcher m = PROJ_PARAM_RE.matcher(s); if (m.matches()) { String key = m.group(2); String val = m.group(3); if (val != null && !"".equals(val)) { key(key).value(val); } else { key(key).value(key); } //key(m.group(1)).value(m.group(2)); } else { key(s).nul(); } //value(s); } endObject(); } endObject(); return this; } //overrides for typing narrowing @Override public JeoJSONWriter object() throws IOException { return (JeoJSONWriter) super.object(); } @Override public JeoJSONWriter array() throws IOException { return (JeoJSONWriter) super.array(); } @Override public JeoJSONWriter key(String key) throws IOException { return (JeoJSONWriter) super.key(key); } @Override public JeoJSONWriter value(Number value) throws IOException { return (JeoJSONWriter) super.value(value); } @Override public JeoJSONWriter value(Object value) throws IOException { return (JeoJSONWriter) super.value(value); } @Override public JeoJSONWriter nul() throws IOException { return (JeoJSONWriter) super.nul(); } @Override public JeoJSONWriter value(String value) throws IOException { return (JeoJSONWriter) super.value(value); } @Override public JeoJSONWriter value(double value) throws IOException { return (JeoJSONWriter) super.value(value); } @Override public JeoJSONWriter value(long value) throws IOException { return (JeoJSONWriter) super.value(value); } @Override public JeoJSONWriter endObject() throws IOException { return (JeoJSONWriter) super.endObject(); } @Override public JeoJSONWriter endArray() throws IOException { return (JeoJSONWriter) super.endArray(); } @Override public JeoJSONWriter flush() throws IOException { return (JeoJSONWriter) super.flush(); } }