blob: 3738243543881c715afc1ab691a0950c563243e6 [file] [log] [blame]
/**
*
*/
package org.eclipse.stem.internal.data.utility;
/*******************************************************************************
* Copyright (c) 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
import java.awt.Polygon;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.swt.graphics.Point;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class finds the centers or central interior points for all
* STEM regions (by ID). It uses the largest polygon
* if their is more than one per region
*
**/
public class PolygonCenterGenerator {
private static final String GML_ID = "gml:id";
private static final String POLYGON = "gml:Polygon";
private static final String BOUNDARY = "gml:outerBoundaryIs";
private static final String LINEARRING = "gml:LinearRing";
private static final String POSLIST = "gml:posList";
private static final String MAP_FILE_SUFFIX = "_MAP.xml";
private static final double RESCALE = 1000000.0;
private static boolean DO_SINGLE_COUNTRY = false;
private static final String SINGLE_COUNTRY = "ITA/";
/**
* Hard coded File reference to the above URI Needed because we don;t know
* how to convert a URI to a file path
*/
public static final String GEO_FOLDER = "../"
+ "org.eclipse.stem.data.geography/"
+ "resources/data/geo/country/";
/**
* Hard coded File reference to the above URI Needed because we don;t know
* how to convert a URI to a file path
*/
public static final String OUTPUT_FOLDER = "./resources/data/country/";
/**
* @param args
*/
public static void main(String[] args) {
discoverCenters();
}
/**
* reads ALL gml data at full resolution and gets the centers for each node
*/
public static final void discoverCenters() {
try {
File f = new File(GEO_FOLDER);
if (f.isDirectory()) {
File[] ccountries = f.listFiles();
if (DO_SINGLE_COUNTRY) {
ccountries = new File[1];
ccountries[0] = new File(GEO_FOLDER+SINGLE_COUNTRY);
}
for (int i = 0; i < ccountries.length; i ++) {
File countryFolder = ccountries[i];
String country = countryFolder.getName();
Map<String, Point> centerMap = new HashMap<String,Point>();
if ((!country.equalsIgnoreCase("ZZZ"))||(!country.equalsIgnoreCase("CVS")) ) {
if (countryFolder.isDirectory()) {
File[] files = countryFolder.listFiles();
// we may have multiple gml files at various resolutions
for (int j = 0; j < files.length; j++) {
File file = files[j];
String name = file.getName();
int idx = name.indexOf(MAP_FILE_SUFFIX);
if(idx>=1) {
// String mapLevel = name.substring(0, idx);
addPolygonCentersGML(file, centerMap);
}
}// for j xml files at different resolutions
writeCenters(centerMap, country);
}// if dir
}// if real country
}// for countries
}// if dir
} catch (Exception e) {
// catch io errors from FileInputStream or readLine()
System.out.println(" IOException error!" + e.getMessage());
e.printStackTrace();
}
}
/**
* reads the GML Map file of polygons keyed by regions.
*
* @param file the gml file to read
* @param centerMap
*/
@SuppressWarnings("null")
public static void addPolygonCentersGML(File file, Map<String, Point> centerMap) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = null;
try {
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(file);
} catch (Exception e) {
System.out.println("Error parsing file " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
//
// MEMBER could have in it Road, Rail, etc...
//
Element route = doc.getDocumentElement();
NodeList allNodes = route.getElementsByTagName(POLYGON);
for (int i = 0; i < allNodes.getLength(); i++) {
Element polygonElt = (Element) allNodes.item(i);
String id = polygonElt.getAttribute(GML_ID);
Polygon poly = null;
double largestArea = -1.0;
/*
* <gml:Polygon gml:id="MX-TAB"> <gml:outerBoundaryIs>
* <gml:LinearRing> <gml:posList>
*/
// default
NodeList boundaryList = polygonElt.getElementsByTagName(BOUNDARY);
for (int ii = 0; ii < boundaryList.getLength(); ii++) {
Element boundary = (Element) boundaryList.item(ii);
NodeList ringList = boundary.getElementsByTagName(LINEARRING);
for (int j = 0; j < ringList.getLength(); j++) {
Element ring = (Element) ringList.item(j);
NodeList posList = ring.getElementsByTagName(POSLIST);
for (int jj = 0; jj < posList.getLength(); jj++) {
Element points = (Element) posList.item(jj);
if (points != null) {
Node tn = points.getFirstChild();
String latlong = tn.getTextContent();
// add the polygon to the map
Polygon testPoly = getPolygon(latlong);
double area = Math.abs(getArea(testPoly));
if(area >= largestArea) {
largestArea = area;
poly = new Polygon(testPoly.xpoints, testPoly.ypoints, testPoly.npoints);
//System.out.println("Found "+id+" area "+area);
}// if largest polygon
}// if have more polygons
}// all polygons
}// all boundaries
}// all rings
//double finalArea = Math.abs(getArea(poly));
//System.out.println("Picked "+id+" area "+finalArea);
//System.out.println(" ");
Point p = getInteriorCentroid(poly);
centerMap.put(id,p);
}// all nodes
System.out.println("Read " + centerMap.size() + " polygons in file ");
return;
}// readMapPolygonsGML()
/**
* create a polygon
* lat lng units for paths and polygons are (int) degrees x 1000000
* @param data
* @return the polygon as a string of lat long data
*/
public static Polygon getPolygon(String data) {
StringTokenizer st = new StringTokenizer(data);
ArrayList<Double> latList = new ArrayList<Double>();
ArrayList<Double> lngList = new ArrayList<Double>();
while (st.hasMoreTokens()) {
String lat = st.nextToken();
String lng = st.nextToken();
latList.add(new Double(lat));
lngList.add(new Double(lng));
}
// make sure the polygon is closed
if ((latList.get(latList.size()-1)!= latList.get(0))
||
(lngList.get(lngList.size()-1)!= lngList.get(0))){
latList.add(latList.get(0));
lngList.add(lngList.get(0));
}
int size = latList.size();
int[] lt = new int[size];
int[] lg = new int[size];
for (int i = 0; i < size; i ++) {
lt[i] = (int) (RESCALE * latList.get(i).doubleValue());
lg[i] = (int) (RESCALE * lngList.get(i).doubleValue());
}
return new Polygon(lt, lg, size);
}// getPolygon
/**
* finds the area of a polgon - exact
* @param p
* @return the area
*/
public static double getArea(Polygon p) {
double area = 0;
int[] x = p.xpoints;
int[] y = p.ypoints;
for (int i = 0; i < x.length-1; i ++) {
area += (((double)x[i]*(double)y[i+1]) - ((double)x[i+1]*(double)y[i]));
}
area /= 2.0;
return area;
}
/**
* finds and returns an internal "central" point
* in the polygon. Not the centriod which could be outside the boundary
* for u shaped regions
* @param p
* @return the "center"
*/
public static Point getInteriorCentroid(Polygon p) {
double maxRange = -1.0;
double cx = 0;
double cy = 0;
Rectangle r = p.getBounds();
final double STEP = 100.0;
double deltax = Math.abs((double) r.width);
double deltay = Math.abs((double) r.height);
deltax /= STEP;
deltay /= STEP;
//System.out.println("Dwidth,Dheight = "+deltax+", "+deltay);
double x = r.getMinX();
double y = r.getMinY();
for (int i = 0; i <= (int) STEP; i ++) {
y = r.getMinY();
for(int j = 0; j <= (int)STEP; j ++) {
if(p.contains(x, y)) {
double range = getSqrdEdgeRange(x,y,p);
if (range >= maxRange) {
maxRange = range;
cx = x;
cy = y;
}
}
y += deltay;
}
x += deltax;
}
//System.out.println("initial estimate "+cx+", "+cy);
// now step back on step and do 200 more steps
// each 100x smaller for a scan resolution of 1/10000
x = cx-deltax;
y = cy-deltay;
double y0 = y;
deltax /= STEP;
deltay /= STEP;
for (int i = 0; i <= (int)(2.0* STEP); i ++) {
y = y0;
for(int j = 0; j <= (int)(2.0*STEP); j ++) {
if(p.contains(x, y)) {
double range = getSqrdEdgeRange(x,y,p);
if (range >= maxRange) {
maxRange = range;
cx = x;
cy = y;
}
}
y += deltay;
}
x += deltax;
}
int ix = (int)Math.round(cx);
int iy = (int)Math.round(cy);
Point center = new Point(ix,iy);
return center;
}// get inner centroid
/**
* for testing
* @param map
*/
public static void printMap(Map<String, Point> map) {
for (Entry<String,Point> entry : map.entrySet()) {
String id = entry.getKey();
Point p = entry.getValue();
System.out.println("id: "+id+" "+p.x+", "+p.y);
}
}
/**
* Create the .properties file
* @param map
* @param country
*/
public static void writeCenters(Map<String, Point> map, String country) {
//1. make the direc
File outFile = new File(OUTPUT_FOLDER+country+"/"+country+"_centers.properties");
try {
System.out.println("writing centers "+outFile.getName()+" = "+outFile.getAbsolutePath());
FileWriter fw = new FileWriter(outFile);
fw.write("# "+ country+"_centers.properties \n");
fw.write("# all regions centers (id, lat, long) \n");
fw.write("\n");
ArrayList<String> lvl0Keys = new ArrayList<String>();
ArrayList<String> lvl1Keys = new ArrayList<String>();
ArrayList<String> lvl2Keys = new ArrayList<String>();
Iterator<String> iter = map.keySet().iterator();
// int icount = 0;
// sort the keys by admin level
while((iter!=null)&&(iter.hasNext())) {
// icount ++;
String id = iter.next();
if(id.indexOf("-")==-1) {
lvl0Keys.add(id);
} else {
int idx = id.indexOf("-");
String test = id.substring(idx+1,id.length());
if(test.indexOf("-")==-1) {
lvl1Keys.add(id);
} else {
lvl2Keys.add(id);
}
}
}
//
fw.write("# level 0 \n");
if (lvl0Keys.size() >=1) {
writeLevelData(lvl0Keys, map, fw);
}// level 0
//
fw.write("\n");
fw.write("# level 1 \n");
if (lvl1Keys.size() >=1) {
writeLevelData(lvl1Keys, map, fw);
}// level 1
fw.write("\n");
fw.write("# level 2 \n");
if (lvl2Keys.size() >=1) {
writeLevelData(lvl2Keys, map, fw);
}// level 2
fw.flush();
fw.close();
} catch(IOException ioe) {
System.out.println("error writing file"+ioe.getMessage());
System.exit(1);
}
}// writeCenters
/**
*
* @param lvlKeys
* @param map
* @param fw
*/
public static void writeLevelData(List<String> lvlKeys, Map<String, Point> map, FileWriter fw ) {
String[] keys = new String[lvlKeys.size()];
for (int i = 0; i < lvlKeys.size(); i ++) {
keys[i] = lvlKeys.get(i);
}
Arrays.sort(keys);
try {
for (int i = 0; i < lvlKeys.size(); i ++) {
Point p = map.get(keys[i]);
double x = p.x;
double y = p.y;
x/= RESCALE;
y/= RESCALE;
fw.write(keys[i]+" = "+x+", "+y+"\n");
}
} catch(IOException ioe) {
System.out.println("error writing file"+ioe.getMessage());
System.exit(1);
}
}// writeLevelData
/**
* finds the range to the nearest edge point
* @param qx
* @param qy
* @param p
* @return range
*/
public static double getSqrdEdgeRange(double qx, double qy, Polygon p) {
double range = Double.MAX_VALUE;
int[] ix = p.xpoints;
int[] iy = p.ypoints;
for (int i=0;i<ix.length;i++) {
double x = ix[i];
double y = iy[i];
double dx = qx-x;
double dy = qy-y;
double r = (dx*dx) + (dy*dy);
if (r <= range) range = r;
}
return range;
}
}