/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.data.mapinfo.mif.geometry; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.data.mapinfo.ProjectionUtils; import org.geotoolkit.data.mapinfo.mif.style.Brush; import org.geotoolkit.data.mapinfo.mif.style.Pen; import org.geotoolkit.util.NamesExt; import org.opengis.util.GenericName; import org.opengis.referencing.operation.MathTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.InputMismatchException; import java.util.List; import java.util.Scanner; import java.util.logging.Level; import org.apache.sis.feature.DefaultAttributeType; import org.apache.sis.feature.FeatureExt; import org.apache.sis.geometry.Envelope2D; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.NullArgumentException; import org.geotoolkit.data.mapinfo.mif.MIFUtils; import org.opengis.feature.AttributeType; import org.opengis.feature.Feature; import static org.geotoolkit.data.mapinfo.mif.style.Brush.BRUSH; import static org.geotoolkit.data.mapinfo.mif.style.Pen.PEN; import org.geotoolkit.geometry.jts.JTS; /** * The class used to build Feature from MIF rectangle or MIF Round rectangle. * * @author Alexis Manin (Geomatys) * Date : 27/02/13 */ public class MIFRectangleBuilder extends MIFGeometryBuilder { public GenericName NAME = NamesExt.create("RECTANGLE"); public static final GenericName ROUND_NAME = NamesExt.create("ROUNDING"); private static final AttributeType ROUNDING = new DefaultAttributeType(Collections.singletonMap("name", ROUND_NAME), Float.class, 1, 1, null); @Override public void buildGeometry(Scanner scanner, Feature toFill, MathTransform toApply) throws DataStoreException { final double[] pts = new double[4]; try { for (int i = 0; i < pts.length; i++) { pts[i] = Double.parseDouble(scanner.next(ProjectionUtils.DOUBLE_PATTERN)); } final CoordinateSequence seq; if(toApply != null) { try { double[] afterT = new double[4]; toApply.transform(pts, 0, afterT, 0, 2); seq = new PackedCoordinateSequence.Double(afterT, 2); } catch (Exception e) { throw new DataStoreException("Unable to transform geometry", e); } } else { seq = new PackedCoordinateSequence.Double(pts, 2); } final Envelope env = new Envelope(seq.getCoordinate(0), seq.getCoordinate(1)); toFill.setPropertyValue(MIFUtils.findGeometryProperty(toFill.getType()).getName().tip().toString(), JTS.toGeometry(env)); } catch (InputMismatchException ex) { throw new DataStoreException("Rectangle is not properly defined : not enough points found.", ex); } if(scanner.hasNext(ProjectionUtils.DOUBLE_PATTERN)) { toFill.setPropertyValue(ROUND_NAME.toString(),Float.parseFloat(scanner.next(ProjectionUtils.DOUBLE_PATTERN))); } if(scanner.hasNext(Pen.PEN_PATTERN) && toFill.getType().getProperties(true).contains(PEN)) { String args = scanner.next()+scanner.nextLine(); String[] argsTab = args.substring(args.indexOf('(')+1, args.length()-1) .replaceAll("[^\\d^,]+", "") .split(","); if (argsTab.length < 3) { LOGGER.log(Level.WARNING, "A PEN tag have been found, but can't be read (bad syntax ?). Ignore style."); } else { final int width = Integer.decode(argsTab[0]); final int pattern = Integer.decode(argsTab[1]); final int color = Integer.decode(argsTab[2]); Pen pen = new Pen(width, pattern, color); toFill.setPropertyValue(Pen.NAME.toString(),pen); } } if(scanner.hasNext(Brush.BRUSH_PATTERN) && toFill.getType().getProperties(true).contains(BRUSH)) { String args = scanner.next()+scanner.nextLine(); String[] argsTab = args.substring(args.indexOf('(')+1, args.length()-1) .replaceAll("[^\\d^,]+", "") .split(","); if (argsTab.length < 2) { LOGGER.log(Level.WARNING, "A BRUSH tag have been found, but can't be read (bad syntax ?). Ignore style."); } else { final int pattern = Integer.decode(argsTab[0]); final int foreground = Integer.decode(argsTab[1]); Brush brush = new Brush(pattern, foreground); if(argsTab.length > 2) { final int background = Integer.decode(argsTab[2]); brush.setBackgroundCC(background); } toFill.setPropertyValue(Brush.NAME.toString(),brush); } } } @Override public String toMIFSyntax(Feature source) throws DataStoreException { ArgumentChecks.ensureNonNull("Source feature", source); if(!FeatureExt.hasAGeometry(source.getType())) { throw new DataStoreException("Input feature does not contain any geometry."); } StringBuilder builder = new StringBuilder(NAME.tip().toString()).append(' '); appendMIFEnvelope(builder, MIFUtils.getGeometryValue(source)); builder.append('\n'); final Object round = MIFUtils.getPropertySafe(source, ROUND_NAME.toString()); if(round != null) { builder.append(round).append('\n'); } final Object pen = MIFUtils.getPropertySafe(source, Pen.NAME.toString()); if (pen instanceof Pen) { builder.append(pen).append('\n'); } final Object brush = MIFUtils.getPropertySafe(source, Brush.NAME.toString()); if (brush instanceof Brush) { builder.append(brush).append('\n'); } return builder.toString(); } /** * Append MIF-MID representation of given object in input string builder. * @param toAppendInto The builder to add data into. * @param sourceEnvelope The object which represents the envelope to write. * @throws NullPointerException If any input is null. * @throws DataStoreException If input object is not manageable for now. For * an overview of possible types, see {@link #getPossibleBindings() }. */ protected void appendMIFEnvelope(final StringBuilder toAppendInto, Object sourceEnvelope) throws DataStoreException, NullPointerException { if (sourceEnvelope instanceof Geometry) sourceEnvelope = ((Geometry) sourceEnvelope).getEnvelopeInternal(); if (sourceEnvelope instanceof com.esri.core.geometry.Envelope) { final com.esri.core.geometry.Envelope env = (com.esri.core.geometry.Envelope) sourceEnvelope; toAppendInto.append(env.getXMin()).append(' ') .append(env.getYMin()).append(' ') .append(env.getXMax()).append(' ') .append(env.getYMax()); } else if (sourceEnvelope instanceof Envelope) { Envelope env = (Envelope) sourceEnvelope; toAppendInto.append(env.getMinX()).append(' ') .append(env.getMinY()).append(' ') .append(env.getMaxX()).append(' ') .append(env.getMaxY()); } else if (sourceEnvelope instanceof Rectangle2D) { Rectangle2D rect = (Rectangle2D) sourceEnvelope; toAppendInto.append(rect.getMinX()).append(' ') .append(rect.getMinY()).append(' ') .append(rect.getMaxX()).append(' ') .append(rect.getMaxY()); } else if (sourceEnvelope instanceof Envelope2D) { Envelope2D env = (Envelope2D) sourceEnvelope; toAppendInto.append(env.getMinX()).append(' ') .append(env.getMinY()).append(' ') .append(env.getMaxX()).append(' ') .append(env.getMaxY()); } else if (sourceEnvelope == null) { throw new NullArgumentException("Input envelope is null !"); } else { throw new DataStoreException("Unable to build a rectangle with the current geometry (Non compatible type" + sourceEnvelope.getClass() + ")."); } } @Override public Class getGeometryBinding() { return Geometry.class; // TODO : replace with esri envelope once rendering engine supports it. } @Override public Class[] getPossibleBindings() { return new Class[]{Geometry.class, com.esri.core.geometry.Envelope.class, Envelope.class, Envelope2D.class, Rectangle2D.class}; } @Override public GenericName getName() { return NAME; } @Override protected List<AttributeType> getAttributes() { final List<AttributeType> descList = new ArrayList<>(3); descList.add(ROUNDING); descList.add(PEN); descList.add(BRUSH); return descList; } }