org.hortonmachine.gears.io.grasslegacy.utils.Window Maven / Gradle / Ivy
/*
* JGrass - Free Open Source Java GIS http://www.jgrass.org
* (C) HydroloGIS - www.hydrologis.com
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package org.hortonmachine.gears.io.grasslegacy.utils;
import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.StringTokenizer;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
/**
*
* The window object represents a particular geographic regione, containing also the information of
* the grid's resolution and therefore also the number of rows and cols of the region.
*
*
* Warning: since the rows and cols are integers, the resolution is recalculated to fulfill
* this. Therefore the asked resolution could not be exactly the one in the end is supplied.
*
*
* @author Andrea Antonello - www.hydrologis.com
*/
public class Window extends Object {
public static final String BLACKBOARD_KEY = "eu.hydrologis.jgrass.libs.region"; //$NON-NLS-1$
public final static String BLANK_WINDOW = "proj: 0\nzone: 0\nnorth: 1\nsouth: 0\nwest: 0\neast: 1\nrows: 1\ncols: 1\ne-w resol: 1\nn-s resol: 1";
private int proj = 0;
private int zone = 0;
private double n = -9999.0;
private double s = -9999.0;
private double w = -9999.0;
private double e = -9999.0;
private double ns_res = -9999.0;
private double we_res = -9999.0;
private int rows = 0;
private int cols = 0;
private LinkedHashMap additionalGrassEntries;
/**
* Creates a new instance of Window by supplying a JGrass workspace WIND file
*
* @param windowFile a JGrass workspace WIND file path
*/
public Window( String windowFile ) {
readWindowFile(windowFile, this);
}
/**
* Creates a new instance of Window from the boundaries and the number of rows and cols
*
* @param west west coord
* @param east east coord
* @param south south coord
* @param north north coord
* @param _rows required number of rows
* @param _cols required number of cols
*/
public Window( double west, double east, double south, double north, int _rows, int _cols ) {
w = west;
e = east;
s = south;
n = north;
rows = _rows;
cols = _cols;
fixResolution();
}
/**
* Creates a new instance of Window
*/
public Window( double west, double east, double south, double north, double weres, double nsres ) {
w = west;
e = east;
s = south;
n = north;
we_res = weres;
ns_res = nsres;
// System.out.println("WINDOW 2 - west=" + w + ", east=" + e + ",
// south=" + s + ", north=" + n
// + ", we_res=" + we_res + ", ns_res=" + ns_res);
fixRowsAndCols();
fixResolution();
}
/**
* Creates a new instance of Window from another window object
*
* @param win a window object
*/
public Window( Window win ) {
w = win.getWest();
e = win.getEast();
s = win.getSouth();
n = win.getNorth();
rows = win.getRows();
cols = win.getCols();
fixResolution();
}
public Window( String west, String east, String south, String north, String ewres, String nsres ) {
double no = -1.0;
double so = -1.0;
double ea = -1.0;
double we = -1.0;
double xres = -1.0;
double yres = -1.0;
if (north.indexOf("N") != -1 || north.indexOf("n") != -1) {
north = north.substring(0, north.length() - 1);
no = degreeToNumber(north);
} else if (north.indexOf("S") != -1 || north.indexOf("s") != -1) {
north = north.substring(0, north.length() - 1);
no = -degreeToNumber(north);
} else {
no = Double.parseDouble(north);
}
if (south.indexOf("N") != -1 || south.indexOf("n") != -1) {
south = south.substring(0, south.length() - 1);
so = degreeToNumber(south);
} else if (south.indexOf("S") != -1 || south.indexOf("s") != -1) {
south = south.substring(0, south.length() - 1);
so = -degreeToNumber(south);
} else {
so = Double.parseDouble(south);
}
if (west.indexOf("E") != -1 || west.indexOf("e") != -1) {
west = west.substring(0, west.length() - 1);
we = degreeToNumber(west);
} else if (west.indexOf("W") != -1 || west.indexOf("w") != -1) {
west = west.substring(0, west.length() - 1);
we = -degreeToNumber(west);
} else {
we = Double.parseDouble(west);
}
if (east.indexOf("E") != -1 || east.indexOf("e") != -1) {
east = east.substring(0, east.length() - 1);
ea = degreeToNumber(east);
} else if (east.indexOf("W") != -1 || east.indexOf("w") != -1) {
east = east.substring(0, east.length() - 1);
ea = -degreeToNumber(east);
} else {
ea = Double.parseDouble(east);
}
if (ewres.indexOf(':') != -1) {
xres = degreeToNumber(ewres);
} else {
xres = Double.parseDouble(ewres);
}
if (nsres.indexOf(':') != -1) {
yres = degreeToNumber(nsres);
} else {
yres = Double.parseDouble(nsres);
}
Window tmp = new Window(we, ea, so, no, xres, yres);
setExtent(tmp);
}
private double degreeToNumber( String value ) {
double number = -1;
String[] valueSplit = value.trim().split(":");
if (valueSplit.length == 3) {
// deg:min:sec.ss
double deg = Double.parseDouble(valueSplit[0]);
double min = Double.parseDouble(valueSplit[1]);
double sec = Double.parseDouble(valueSplit[2]);
number = deg + min / 60.0 + sec / 60.0 / 60.0;
} else if (valueSplit.length == 2) {
// deg:min
double deg = Double.parseDouble(valueSplit[0]);
double min = Double.parseDouble(valueSplit[1]);
number = deg + min / 60.0;
} else if (valueSplit.length == 1) {
// deg
number = Double.parseDouble(valueSplit[0]);
}
return number;
}
public Window( String west, String east, String south, String north, int rows, int cols ) {
double no = -1.0;
double so = -1.0;
double ea = -1.0;
double we = -1.0;
if (north.indexOf("N") != -1 || north.indexOf("n") != -1) {
north = north.substring(0, north.length() - 1);
no = degreeToNumber(north);
} else if (north.indexOf("S") != -1 || north.indexOf("s") != -1) {
north = north.substring(0, north.length() - 1);
no = -degreeToNumber(north);
} else {
no = Double.parseDouble(north);
}
if (south.indexOf("N") != -1 || south.indexOf("n") != -1) {
south = south.substring(0, south.length() - 1);
so = degreeToNumber(south);
} else if (south.indexOf("S") != -1 || south.indexOf("s") != -1) {
south = south.substring(0, south.length() - 1);
so = -degreeToNumber(south);
} else {
so = Double.parseDouble(south);
}
if (west.indexOf("E") != -1 || west.indexOf("e") != -1) {
west = west.substring(0, west.length() - 1);
we = degreeToNumber(west);
} else if (west.indexOf("W") != -1 || west.indexOf("w") != -1) {
west = west.substring(0, west.length() - 1);
we = -degreeToNumber(west);
} else {
we = Double.parseDouble(west);
}
if (east.indexOf("E") != -1 || east.indexOf("e") != -1) {
east = east.substring(0, east.length() - 1);
ea = degreeToNumber(east);
} else if (east.indexOf("W") != -1 || east.indexOf("w") != -1) {
east = east.substring(0, east.length() - 1);
ea = -degreeToNumber(east);
} else {
ea = Double.parseDouble(east);
}
Window tmp = new Window(we, ea, so, no, rows, cols);
setExtent(tmp);
}
/**
* @return the JTS envelope from the window object
*/
public Envelope getEnvelope() {
return new Envelope(new Coordinate(w, n), new Coordinate(e, s));
}
/**
*
*/
public void limitRowsCols( int _rows, int _cols ) {
/* If current rows > _rows then clamp the value. */
if (rows > _rows) {
rows = _rows;
}
if (cols > _cols) {
cols = _cols;
}
fixResolution();
}
/**
* Sets the extent of this window using another window.
*
* @param win another window object
*/
public void setExtent( Window win ) {
w = win.getWest();
e = win.getEast();
n = win.getNorth();
s = win.getSouth();
rows = win.getRows();
cols = win.getCols();
fixResolution();
fixRowsAndCols();
}
/**
* @return the rectangle wrapping the window object
*/
public java.awt.geom.Rectangle2D.Double getRectangle() {
return new java.awt.geom.Rectangle2D.Double(w, s, getXExtent(), getYExtent());
}
@SuppressWarnings("nls")
public String toString() {
return ("window:\nwest=" + w + "\neast=" + e + "\nsouth=" + s + "\nnorth=" + n + "\nwe_res=" + we_res + "\nns_res="
+ ns_res + "\nrows=" + rows + "\ncols=" + cols);
}
/**
* fix the resolution with the integer rounded rows and cols
*/
public void fixResolution() {
we_res = (e - w) / cols;
ns_res = (n - s) / rows;
}
/**
* calculate rows and cols from the region and its resolution. Round if required.
*/
public void fixRowsAndCols() {
rows = (int) Math.round((n - s) / ns_res);
if (rows < 1)
rows = 1;
cols = (int) Math.round((e - w) / we_res);
if (cols < 1)
cols = 1;
}
/*
* some getters and setters
*/
public int getProj() {
return proj;
}
public void setProj( int _proj ) {
proj = _proj;
}
public int getZone() {
return zone;
}
public void setZone( int _zone ) {
zone = _zone;
}
public double getWest() {
return w;
}
public double getEast() {
return e;
}
public double getSouth() {
return s;
}
public double getNorth() {
return n;
}
public int getRows() {
return rows;
}
public int getCols() {
return cols;
}
public double getWEResolution() {
return we_res;
}
public double getNSResolution() {
return ns_res;
}
public double getXExtent() {
return e - w;
}
public double getYExtent() {
return n - s;
}
public void setCols( int cols ) {
this.cols = cols;
}
public void setEast( double e ) {
this.e = e;
}
public void setNorth( double n ) {
this.n = n;
}
public void setNSResolution( double ns_res ) {
this.ns_res = ns_res;
}
public void setRows( int rows ) {
this.rows = rows;
}
public void setSouth( double s ) {
this.s = s;
}
public void setWest( double w ) {
this.w = w;
}
public void setWEResolution( double we_res ) {
this.we_res = we_res;
}
public void setAdditionalGrassEntries( LinkedHashMap adds ) {
this.additionalGrassEntries = adds;
}
public LinkedHashMap getAdditionalGrassEntries() {
return additionalGrassEntries;
}
/**
* Moves the point given by X and Y to be on the grid of the active region.
*
* @param x the easting of the arbitrary point
* @param y the northing of the arbitrary point
* @param activeWindow the active window from which to take the grid
* @return the snapped point
*/
public static Point2D.Double snapToNextHigherInActiveRegionResolution( double x, double y, Window activeWindow ) {
double minx = activeWindow.getRectangle().getBounds2D().getMinX();
double ewres = activeWindow.getWEResolution();
double xsnap = minx + (Math.ceil((x - minx) / ewres) * ewres);
double miny = activeWindow.getRectangle().getBounds2D().getMinY();
double nsres = activeWindow.getNSResolution();
double ysnap = miny + (Math.ceil((y - miny) / nsres) * nsres);
return new Point2D.Double(xsnap, ysnap);
}
/**
* Get the active region window from the mapset
*
* @param mapsetPath the path to the mapset folder
* @return the active Window object of that mapset
*/
public static Window getActiveWindowFromMapset( String mapsetPath ) {
File windFile = new File(mapsetPath + File.separator + GrassLegacyConstans.WIND);
if (!windFile.exists()) {
return null;
}
return new Window(windFile.getAbsolutePath());
}
/**
* Write active region window to the mapset
*
* @param mapsetPath the path to the mapset folder
* @param the active Window object
* @throws IOException
*/
public static void writeActiveWindowToMapset( String mapsetPath, Window window ) throws IOException {
writeWindowFile(mapsetPath + File.separator + GrassLegacyConstans.WIND, window);
}
/**
* Write default region window to the PERMANENT mapset
*
* @param locationPath the path to the location folder
* @param the active Window object
* @throws IOException
*/
public static void writeDefaultWindowToLocation( String locationPath, Window window ) throws IOException {
writeWindowFile(locationPath + File.separator + GrassLegacyConstans.PERMANENT_MAPSET + File.separator
+ GrassLegacyConstans.DEFAULT_WIND, window);
}
/**
* Takes an envelope and an active region and creates a new region to match the bounds of the
* envelope, but the resolutions of the active region. This is important if the region has to
* match some feature layer. The bounds are assured to contain completely the envelope.
*
* @param bounds
* @param activeRegion
*/
public static Window adaptActiveRegionToEnvelope( Envelope bounds, Window activeRegion ) {
Point2D eastNorth = Window.snapToNextHigherInActiveRegionResolution(bounds.getMaxX(), bounds.getMaxY(), activeRegion);
Point2D westsouth = Window.snapToNextHigherInActiveRegionResolution(bounds.getMinX() - activeRegion.getWEResolution(),
bounds.getMinY() - activeRegion.getNSResolution(), activeRegion);
Window tmp = new Window(westsouth.getX(), eastNorth.getX(), westsouth.getY(), eastNorth.getY(),
activeRegion.getWEResolution(), activeRegion.getNSResolution());
// activeRegion.setExtent(tmp);
return tmp;
}
/**
* @param filePath the path to the window file
* @param w the resulting window object
*/
@SuppressWarnings("nls")
private static Throwable readWindowFile( String filePath, Window w ) {
String line;
try {
BufferedReader windReader;
windReader = new BufferedReader(new FileReader(filePath));
LinkedHashMap store = new LinkedHashMap();
while( (line = windReader.readLine()) != null ) {
line = line.replaceFirst(":", "@@@@");
StringTokenizer tok = new StringTokenizer(line, "@@@@"); //$NON-NLS-1$
if (tok.countTokens() == 2) {
String key = tok.nextToken().trim();
String value = tok.nextToken().trim();
/*
* If key is 'e-w res' or 'n-s resol' or 'res3' then store 'xxx resol'
*/
// this is to keep compatibility with GRASS, which seems to
// have changed
if ((key.indexOf("res") != -1 && key.indexOf("resol") == -1) //$NON-NLS-1$ //$NON-NLS-2$
|| key.indexOf("res3") != -1) { //$NON-NLS-1$
if (!key.startsWith("compressed")) //$NON-NLS-1$
store.put(key.replaceAll("res", "resol"), value); //$NON-NLS-1$ //$NON-NLS-2$
} else {
store.put(key, value);
}
}
}
try {
w.setProj(Integer.parseInt(store.get("proj"))); //$NON-NLS-1$
w.setZone(Integer.parseInt(store.get("zone"))); //$NON-NLS-1$
store.remove("proj");
store.remove("zone");
} catch (Exception e) {
e.printStackTrace();
}
Window tmpWindow = null;
if (store.containsKey("n-s res")) { //$NON-NLS-1$
tmpWindow = new Window(store.get("west"), //$NON-NLS-1$
store.get("east"), //$NON-NLS-1$
store.get("south"), //$NON-NLS-1$
store.get("north"), //$NON-NLS-1$
store.get("e-w res"), store.get("n-s res")); //$NON-NLS-1$
store.remove("e-w resol");
store.remove("n-s resol");
} else if (store.containsKey("cols")) { //$NON-NLS-1$
tmpWindow = new Window(store.get("west"), //$NON-NLS-1$
store.get("east"), //$NON-NLS-1$
store.get("south"), //$NON-NLS-1$
store.get("north"), //$NON-NLS-1$
Integer.parseInt(store.get("rows")), //$NON-NLS-1$
Integer.parseInt(store.get("cols"))); //$NON-NLS-1$
store.remove("cols");
store.remove("rows");
} else {
throw new RuntimeException();
}
store.remove("north");
store.remove("south");
store.remove("east");
store.remove("west");
// what is noty needed in JGrass is needed in GRASS, so keep it
tmpWindow.setAdditionalGrassEntries(store);
w.setExtent(tmpWindow);
windReader.close();
windReader = null;
store = null;
} catch (FileNotFoundException e) {
return new RuntimeException().initCause(e);
} catch (IOException e) {
return new RuntimeException().initCause(e);
}
return null;
}
/**
* Reads a text file and changes only the window region values using the window object supplied.
*
* @param filePath the path to the WIND file
* @param w the window to set
* @return a throwable object to trace if everything went well
* @throws IOException
*/
private static void writeWindowFile( String filepath, Window w ) throws IOException {
String line;
File file = new File(filepath);
if (!file.exists()) {
/*
* if on vfat filesystem it could be a problem of case, often
* happens with WIND file. So at least try that check.
*/
String nameLower = file.getName().toLowerCase();
String nameUpper = file.getName().toUpperCase();
String baseDir = file.getParent();
File tmpFile = null;
if ((tmpFile = new File(baseDir + File.separator + nameLower)).exists()) {
file = tmpFile;
} else if ((tmpFile = new File(baseDir + File.separator + nameUpper)).exists()) {
file = tmpFile;
} else {
// ok, file doesn't really exist, just create a blank window first
BufferedWriter out = new BufferedWriter(new FileWriter(file));
out.write(BLANK_WINDOW);
out.close();
}
}
BufferedReader windReader = new BufferedReader(new FileReader(file));
LinkedHashMap store = new LinkedHashMap();
while( (line = windReader.readLine()) != null ) {
StringTokenizer tok = new StringTokenizer(line, ":"); //$NON-NLS-1$
if (tok.countTokens() == 2) {
String key = tok.nextToken().trim();
String value = tok.nextToken().trim();
/*
* this is now corrected, since GRASS seems to support only resol from 6.2 on
*/
if ((key.indexOf("res") != -1 && key.indexOf("resol") == -1) //$NON-NLS-1$ //$NON-NLS-2$
|| key.indexOf("res3") != -1) { //$NON-NLS-1$
store.put(key.replaceAll("res", "resol"), value); //$NON-NLS-1$ //$NON-NLS-2$
} else
store.put(key, value);
}
}
/*
* Now overwrite the window region entries using the values in the supplied window
* object.
*/
store.put("north", new java.lang.Double(w.getNorth()).toString()); //$NON-NLS-1$
store.put("south", new java.lang.Double(w.getSouth()).toString()); //$NON-NLS-1$
store.put("east", new java.lang.Double(w.getEast()).toString()); //$NON-NLS-1$
store.put("west", new java.lang.Double(w.getWest()).toString()); //$NON-NLS-1$
store.put("n-s resol", new java.lang.Double(w.getNSResolution()).toString()); //$NON-NLS-1$
store.put("e-w resol", new java.lang.Double(w.getWEResolution()).toString()); //$NON-NLS-1$
store.put("cols", new java.lang.Integer(w.getCols()).toString()); //$NON-NLS-1$
store.put("rows", new java.lang.Integer(w.getRows()).toString()); //$NON-NLS-1$
windReader.close();
windReader = null;
/* Now write the data back to the file */
StringBuffer data = new StringBuffer(512);
Iterator it = store.keySet().iterator();
while( it.hasNext() ) {
String key = it.next();
data.append(key + ": " + store.get(key) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
BufferedWriter windWriter = new BufferedWriter(new FileWriter(file));
windWriter.write(data.toString());
windWriter.flush();
windWriter.close();
}
}