/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2012, 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.io.wkt; import java.io.*; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import org.opengis.util.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.io.wkt.Convention; import org.apache.sis.io.wkt.WKTFormat; import org.geotoolkit.lang.Static; import org.apache.sis.referencing.CRS; import org.geotoolkit.resources.Errors; import org.geotoolkit.io.ContentFormatException; import org.apache.sis.io.wkt.Warnings; import static java.nio.file.StandardOpenOption.*; /** * Parses and formats PRJ files. Those files usually have the {@code ".prj"} suffix and contain * the definition of exactly one <cite>Coordinate Reference System</cite> (CRS) in <cite>Well * Known Text</cite> (WKT) format. This class allows the definition to span many lines, but the * common practice is to provide the full WKT string on a single line. * <p> * If the definition is not a valid WKT string (during reads), or if a CRS can not be formatted as * a WKT (during writes), then the methods in this class throw a {@link ContentFormatException}. * This is a subclass of {@link IOException} and consequently does not require a <code>try … * catch</code> block different than the one for normal I/O operations. * <p> * Every files are expected to use the ISO-8859-1 (a.k.a. ISO-LATIN-1) encoding. * * @author Martin Desruisseaux (Geomatys) * @version 3.07 * * @since 3.05 * @module */ public final class PrjFiles extends Static { /** * The encoding of PRJ files. */ private static final String ENCODING = "ISO-8859-1"; /** * Do not allow instantiation of this class. */ private PrjFiles() { } /** * Parses a PRJ file and returns its content as a coordinate reference system. * * @param file The file to parse. It usually has the {@code ".prj"} suffix, * @return The PRJ file content as a coordinate reference system. * @throws ContentFormatException If the content of the PRJ file is an invalid WKT. * @throws IOException If the parsing failed for an other raison. * @deprecated use {@link #read(Path)} instead */ public static CoordinateReferenceSystem read(final File file) throws ContentFormatException, IOException { return read(new FileInputStream(file), true); } /** * Parses a PRJ file and returns its content as a coordinate reference system. * * @param file The file to parse. It usually has the {@code ".prj"} suffix, * @return The PRJ file content as a coordinate reference system. * @throws ContentFormatException If the content of the PRJ file is an invalid WKT. * @throws IOException If the parsing failed for an other raison. */ public static CoordinateReferenceSystem read(final Path file) throws ContentFormatException, IOException { return read(Files.newInputStream(file), true); } /** * Parses a PRJ file from a URL and returns its content as a coordinate reference system. * * @param file The file to parse. It usually has the {@code ".prj"} suffix, * @return The PRJ file content as a coordinate reference system. * @throws ContentFormatException If the content of the PRJ file is an invalid WKT. * @throws IOException If the parsing failed for an other raison. */ public static CoordinateReferenceSystem read(final URL file) throws ContentFormatException, IOException { return read(file.openStream(), true); } /** * Parses a PRJ file from a channel and returns its content as a coordinate reference system. * The given channel is not closed by this method, unless the {@code close} argument is set to * {@code true}. The later case allows this method to close the channel sooner than what could * be achieved if the channel was closed by the caller: before the WKT parsing begin. * * @param in The channel to read. * @param close Whatever this method should close the channel. * @return The PRJ file content as a coordinate reference system. * @throws ContentFormatException If the content of the PRJ file is an invalid WKT. * @throws IOException If the parsing failed for an other raison. * * @since 3.07 */ public static CoordinateReferenceSystem read(final ReadableByteChannel in, final boolean close) throws ContentFormatException, IOException { return read(Channels.newInputStream(in), close); } /** * Parses a PRJ file from a stream and returns its content as a coordinate reference system. * The given stream is not closed by this method, unless the {@code close} argument is set to * {@code true}. The later case allows this method to close the stream sooner than what could * be achieved if the stream was closed by the caller: before the WKT parsing begin. * * @param in The stream to read. * @param close Whatever this method should close the stream. * @return The PRJ file content as a coordinate reference system. * @throws ContentFormatException If the content of the PRJ file is an invalid WKT. * @throws IOException If the parsing failed for an other raison. */ public static CoordinateReferenceSystem read(final InputStream in, final boolean close) throws ContentFormatException, IOException { return read(new BufferedReader(new InputStreamReader(in, ENCODING)), close); } /** * Parses a PRJ file from a reader and returns its content as a coordinate reference system. * The given reader is not closed by this method, unless the {@code close} argument is set to * {@code true}. The later case allows this method to close the reader sooner than what could * be achieved if the reader was closed by the caller: before the WKT parsing begin. * * @param in The reader. * @param close Whatever this method should close the reader. * @return The PRJ file content as a coordinate reference system. * @throws ContentFormatException If the content of the PRJ file is an invalid WKT. * @throws IOException If the parsing failed for an other raison. */ public static CoordinateReferenceSystem read(final BufferedReader in, final boolean close) throws ContentFormatException, IOException { StringBuilder buffer = null; String wkt=null, line; while ((line = in.readLine()) != null) { line = line.trim(); if (!line.isEmpty()) { if (wkt == null) { wkt = line; } else { if (buffer == null) { buffer = new StringBuilder(wkt); } buffer.append('\n').append(line); } } } if (close) { in.close(); } if (buffer != null) { wkt = buffer.toString(); } if (wkt == null) { throw new EOFException(Errors.format(Errors.Keys.EndOfDataFile)); } try { return CRS.fromWKT(wkt); } catch (FactoryException e) { throw new ContentFormatException(e.getLocalizedMessage(), e); } } /** * Formats the given CRS as a string. * * @throws ContentFormatException if the given CRS is not formattable as a WKT. */ private static String format(final CoordinateReferenceSystem crs) throws ContentFormatException { final WKTFormat format = new WKTFormat(null, null); format.setConvention(Convention.WKT1); format.setIndentation(WKTFormat.SINGLE_LINE); final String wkt = format.format(crs); final Warnings warning = format.getWarnings(); if (warning != null) { throw new ContentFormatException(warning.toString()); } return wkt; } /** * Formats a coordinate reference system as a PRJ file. * The file is created only if the given CRS is formattable. * * @param crs The PRJ file content as a coordinate reference system. * @param file The file to create. It usually has the {@code ".prj"} suffix, * @throws ContentFormatException if the given CRS is not formattable as a WKT. * @throws IOException If an other error occurred while writing the file. */ public static void write(final CoordinateReferenceSystem crs, final File file) throws ContentFormatException, IOException { final String wkt = format(crs); // No need to buffer, because we will write everything (except EOL) in one shot. // In addition, OutputStreamWriter already manage its own internal buffer anyway. try (Writer out = new OutputStreamWriter(new FileOutputStream(file), ENCODING)) { out.write(wkt); out.write('\n'); // Use Unix EOL for cross-platform consistency. } } /** * Formats a coordinate reference system as a PRJ file. * The file is created only if the given CRS is formattable. * * @param crs The PRJ file content as a coordinate reference system. * @param file The file to create. It usually has the {@code ".prj"} suffix, * @throws ContentFormatException if the given CRS is not formattable as a WKT. * @throws IOException If an other error occurred while writing the file. */ public static void write(final CoordinateReferenceSystem crs, final Path file) throws ContentFormatException, IOException { final String wkt = format(crs); // No need to buffer, because we will write everything (except EOL) in one shot. // In addition, OutputStreamWriter already manage its own internal buffer anyway. try (Writer out = Files.newBufferedWriter(file, Charset.forName(ENCODING), CREATE, WRITE, TRUNCATE_EXISTING)) { out.write(wkt); out.write('\n'); // Use Unix EOL for cross-platform consistency. } } /** * Formats a coordinate reference system as a PRJ file in the given channel. The channel * is <strong>not</strong> closed by this method. It is caller responsibility to close it. * * @param crs The PRJ file content as a coordinate reference system. * @param out The channel where to write. * @throws ContentFormatException if the given CRS is not formattable as a WKT. * @throws IOException If an other error occurred while writing the file. * * @since 3.07 */ public static void write(final CoordinateReferenceSystem crs, final WritableByteChannel out) throws ContentFormatException, IOException { write(crs, Channels.newOutputStream(out)); } /** * Formats a coordinate reference system as a PRJ file in the given stream. The stream * is <strong>not</strong> closed by this method. It is caller responsibility to close it. * * @param crs The PRJ file content as a coordinate reference system. * @param out The stream where to write. * @throws ContentFormatException if the given CRS is not formattable as a WKT. * @throws IOException If an other error occurred while writing the file. */ public static void write(final CoordinateReferenceSystem crs, final OutputStream out) throws ContentFormatException, IOException { // No need to buffer - see the above method. final Writer writer = new OutputStreamWriter(out, ENCODING); write(crs, writer); writer.flush(); } /** * Formats a coordinate reference system as a PRJ file in the given writer. The writer * is <strong>not</strong> closed by this method. It is caller responsibility to close it. * * @param crs The PRJ file content as a coordinate reference system. * @param out The writer. * @throws ContentFormatException if the given CRS is not formattable as a WKT. * @throws IOException If an other error occurred while writing the file. */ public static void write(final CoordinateReferenceSystem crs, final Writer out) throws ContentFormatException, IOException { out.write(format(crs)); out.write('\n'); // Use Unix EOL for cross-platform consistency. // No close - this is caller responsibility. } }