Package tigerpoly

Source Code of tigerpoly.TigerPoly

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
*
*    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 tigerpoly;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.geotools.data.DataStore;
import org.geotools.data.DefaultQuery;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.SchemaNotFoundException;
import org.geotools.data.memory.MemoryDataStore;
import org.geotools.data.postgis.PostgisDataStore;
import org.geotools.data.postgis.PostgisDataStoreFactory;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureType;
import org.geotools.filter.AttributeExpression;
import org.geotools.filter.AttributeExpressionImpl;
import org.geotools.filter.AttributeExpressionImpl2;
import org.geotools.filter.CompareFilter;
import org.geotools.filter.FilterFactory;
import org.geotools.filter.FilterType;
import org.geotools.filter.IllegalFilterException;


import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.index.strtree.SIRtree;
import com.vividsolutions.jts.index.strtree.STRtree;
import com.vividsolutions.jts.operation.polygonize.Polygonizer;

/**
*  a. load the completechains (lines)  store in hash table that takes TLID -> geometry
*  b. load the polylink (links lines and polygons).  end result is a hashtable that takes a polyid to a list of lines (chains)
*  c. load the PIP file and put in STRTree (spatial index)
*
*  d. for each polygon (set of lines), polygonize. 
*        for each polygon produced, they should enclose at least one PIP (>1 only for holes of the main polygon)
*        the one one with the correct polygon is the result (exactly one pip inside it)
*        if ANY polygon contains != 1 PIP then an error has occured
*
*  e. send back polygon (with module and polyid)
*
*
* @author david blasby
*
* insert into geometry_columns values ('','public','poly2','the_geom',2,1,'POLYGON');
* create table poly2 (the_geom geometry,polyid numeric(10,0), module char(8)) with oids;
*/
public class TigerPoly
{
    String MODULE = "";
  Hashtable completeChains = new Hashtable()// links TLID (Long) to LineString
  Hashtable polyLineSet = new Hashtable(); // links polyid (Long) to ArrayList of Linestring
  STRtree PIP = new STRtree() ;// links in the PIP Points (with internal JTS Long corresponding to polyid)
 
  Hashtable finishedPolys = new Hashtable(); //links POLYID (Long) to Polygon
 
  DataStore ds = null;
 
  boolean allowHolesWithNoPIP = false; // I noticed that some counties (TGR13231,TGR08031,TGR34017,TGR51683) have missing pips.  This
                                      //  normally causes a qa/qc exception, this prevents it.  I recommend against this unless you know
                                      // what you are doing.
 
  /**
   *  adds a line to the polygon line set
   * @param tlidBD
   * @param polyidBD
   */
  public void addToPolylineset(BigDecimal tlidBD, BigDecimal polyidBD)
  {
    Long tlid = new Long(tlidBD.longValue());
    Long polyid = new Long(polyidBD.longValue());
    ArrayList al = (ArrayList)polyLineSet.get(polyid);
    if (al == null)
    {
      al = new ArrayList();
      polyLineSet.put(polyid,al);
    }
    al.add( completeChains.get(tlid) );
  }
 
  /**
   * reads the completechain dataset
   * @throws Exception
   */
  public void getCC() throws Exception
  {

      FilterFactory ff = FilterFactory.createFilterFactory();
      CompareFilter cf = ff.createCompareFilter(FilterType.COMPARE_EQUALS);
        
       
      cf.addLeftValue( ff.createAttributeExpression( null, "module") );
      cf.addRightValue( ff.createLiteralExpression(MODULE));
        
     
      String[] ps = new String []{"wkb_geometry","tlid"};
      Query q = new DefaultQuery("completechain",cf,ps);        
      
        
      FeatureReader fr = ds.getFeatureReader(q, new DefaultTransaction() );
  
        while (fr.hasNext())
        {
          Feature f= fr.next();
         
          BigDecimal tlidBD = (BigDecimal) f.getAttribute(1);
          Long tlid = new Long(tlidBD.longValue());
         
          if (tlid == null)
            throw new Exception("cchain has null tlid");
         
          completeChains.put(tlid, f.getAttribute(0));//linestring
        }
        fr.close();
}
 
  /**
   *
   *  reads the polychainlink dataset
   */
  public void getPolygonLines() throws Exception
  {

      FilterFactory ff = FilterFactory.createFilterFactory();
      CompareFilter cf = ff.createCompareFilter(FilterType.COMPARE_EQUALS);
        
       
      cf.addLeftValue( ff.createAttributeExpression( null, "module") );
      cf.addRightValue( ff.createLiteralExpression(MODULE));
        
     
      String[] ps = new String []{"polyidl","polyidr","tlid"};
      Query q = new DefaultQuery("polychainlink",cf,ps);        
      
        
      FeatureReader fr = ds.getFeatureReader(q, new DefaultTransaction() );
  
        while (fr.hasNext())
        {
          Feature f= fr.next();
          f.getFeatureType();
         
          BigDecimal polyidlBD = (BigDecimal) f.getAttribute(0);
          BigDecimal polyidrBD = (BigDecimal) f.getAttribute(1);
          BigDecimal tlidBD = (BigDecimal) f.getAttribute(2);
         
          if (tlidBD == null)
            throw new Exception("polylink has null tlid");
         
          if ( (polyidlBD == null) && (polyidrBD ==null) )
            throw new Exception("polylink has 2 null polyids");
         
                boolean one_null = ((polyidlBD == null) || (polyidrBD ==null));
                if (one_null ||  (polyidlBD.longValue() != polyidrBD.longValue())) // == means that they are dangles
          {
            if (polyidlBD != null)
              addToPolylineset( tlidBD,  polyidlBD);
            if (polyidrBD != null)
              addToPolylineset( tlidBD,  polyidrBD);
          }
        }
        fr.close();

  }
 
  /**
   * reads the pip dataset
   * @throws Exception
   */
  public void getPIP() throws Exception
  {

      FilterFactory ff = FilterFactory.createFilterFactory();
      CompareFilter cf = ff.createCompareFilter(FilterType.COMPARE_EQUALS);
        
       
      cf.addLeftValue( ff.createAttributeExpression( null, "module") );
      cf.addRightValue( ff.createLiteralExpression(MODULE));
        
     
      String[] ps = new String []{"wkb_geometry","polyid"};
      Query q = new DefaultQuery("pip",cf,ps);        
      
        
      FeatureReader fr = ds.getFeatureReader(q, new DefaultTransaction() );
  
        while (fr.hasNext())
        {
          Feature f= fr.next();
         
          BigDecimal polyidBD = (BigDecimal) f.getAttribute(1);
          Long polyid = new Long(polyidBD.longValue());
         
          if (polyid == null)
            throw new Exception("PIP  has null polyid");
         
          Geometry g = (Geometry) f.getAttribute(0);
          g.setUserData(polyid);
         
          PIP.insert(g.getEnvelopeInternal(),g);
        }
        fr.close();
  }
 
  /**
   *  dbname=tiger2005fe user=postgres host=localhost
   *
   *  parses the PG connect string into geotools postgis datastore params
   *
   * pg_Connect("host=myHost port=myPort  dbname=myDB user=myUser password=myPassword ");
   * @param connectstring
   * @return
   * @throws Exception
   */
  public static Map parsePG(String connectstring) throws Exception
  {
    Map param = new HashMap();
   
    Pattern p =   Pattern.compile(".*host=([^ ]+).*");
    Matcher m = p.matcher(connectstring);
   
    if (m.matches())
    {
      param.put("host",m.group(1));
    }
    else
    {
      param.put("host","localhost");
    }
   
    p =   Pattern.compile(".*user=([^ ]+).*");
    m = p.matcher(connectstring);
   
    if (m.matches())
    {
      param.put("user",m.group(1));
    }
    else
    {
      throw new Exception("PG CONNECT string does not contain a user name.  connect string should look like: 'host=myHost port=myPort  dbname=myDB user=myUser password=myPassword'");
    }
   
    p =   Pattern.compile(".*dbname=([^ ]+).*");
    m = p.matcher(connectstring);
   
    if (m.matches())
    {
      param.put("database",m.group(1));
    }
    else
    {
      throw new Exception("PG CONNECT string does not contain a dbname.  connect string should look like: 'host=myHost port=myPort  dbname=myDB user=myUser password=myPassword'");
    }
   
    p =   Pattern.compile(".*port=([^ ]+).*");
    m = p.matcher(connectstring);
   
    if (m.matches())
    {
      param.put("port",m.group(1));
    }
    else
    {
      param.put("port","5432");
    }
   
    p =   Pattern.compile(".*password=([^ ]+).*");
    m = p.matcher(connectstring);
   
    if (m.matches())
    {
      param.put("passwd",m.group(1));
    }
    else
    {
      //do nothing
    }
 
    return param;
  }
 
 
  public static void main(String[] args)
  {
   
    if ( (args.length !=2) && (args.length !=3) )
    {
      System.out.println("usage: \"host=myHost port=myPort  dbname=myDB user=myUser password=myPassword\" <module name> [allowMissingPIP]");
      System.out.println("dont forget to:");
      System.out.println("1. put quotes around the postgresql connection string");
      System.out.println("2. pre-create the output database table:");
      System.out.println("          create table poly2 (the_geom geometry, polyid numeric(10,0), module char(8)) with oids;");
      System.out.println("             insert into geometry_columns values ('','public','poly2','the_geom',2,1,'POLYGON');");
      System.out.println("");
      System.out.println("allowMissingPIP -- dont set this unless you know what you're doing. TIGER is supposed to be a coverage, but a few groups have 'holes' in the coverage.  There's nothing you can do about it -- setting this option will cause this program NOT to throw qa-qc exceptions in this case. ");
      return;
    }
     
    TigerPoly THIS = new TigerPoly();
   
    if (args.length ==3)
    {
      if (args[2].equalsIgnoreCase("allowMissingPIP" ))
      {
        THIS.allowHolesWithNoPIP = true;
      }
      else
      {
        System.out.println("to turn on 'allowMissingPIP', just put those words after your group.");
      }
    }
   
      PostgisDataStoreFactory  pgdsf = new PostgisDataStoreFactory();
 
       try{
        
        
         Map param =  parsePG(args[0]);
            
       param.put("wkb enabled","true");
       param.put("loose bbox","true");
       param.put("dbtype","postgis");
      
         
           
         THIS.ds = pgdsf.createDataStore(param);
        
           THIS.MODULE = args[1];
        
         System.out.println("start module = "+THIS.MODULE);
          
         System.out.println("loading completechains...");
         THIS.getCC();
         System.out.println("loaded " + THIS.completeChains.size() +" completechains...");
        
         System.out.println("loading polychainlink...");        
         THIS.getPolygonLines();
         System.out.println("loaded " + THIS.polyLineSet.size() +" polygons...");
        
         System.out.println("loading PIP...");        
         THIS.getPIP();
         System.out.println("loaded "  + THIS.PIP.size() +" PIPs...");
        
         if (THIS.polyLineSet.size() !=  THIS.PIP.size())
         {
           throw new Exception("polylineset and PIP should be the same size!");
         }
        
         System.out.println("Processing...");
         THIS.process();
        
         if (THIS.polyLineSet.size() !=  THIS.finishedPolys.size())
         {
           throw new Exception("didnt build enough polygons!");
         }
         System.out.println("writing");
         THIS.write();
        
         System.out.println("done! " +THIS.MODULE);
        
        
       }
       catch (SchemaNotFoundException ee)
     {
         System.out.println("You must create the poly2 table in the postgis database:");
      System.out.println("          create table poly2 (the_geom geometry, polyid numeric(10,0), module char(8)) with oids;");
      System.out.println("             insert into geometry_columns values ('','public','poly2','the_geom',2,1,'POLYGON');");
     }
       catch (Exception e)
     {
         e.printStackTrace();
     }
  }
 
 
  /** write out the results to poly2
   *
   * @throws Exception
   */
  public void write() throws Exception
  {
    FeatureStore fs = (FeatureStore) ds.getFeatureSource("poly2");
    FeatureType ft = fs.getSchema();
   
    MemoryDataStore memorystore =new MemoryDataStore();
   
    Enumeration enum = finishedPolys.elements();
    while (enum.hasMoreElements())
    {
      Polygon p = (Polygon) enum.nextElement();
      Long polyid = ( (Long) p.getUserData());
      Object[] values = new Object[3];
      values[ft.find("module")] = MODULE;
      values[ft.find("the_geom")] = p;
      values[ft.find("polyid")] = polyid;
      Feature f = ft.create(values);
      memorystore.addFeature(f);
    }
    fs.addFeatures(memorystore.getFeatureReader("poly2"));
  }

  /**
   *  build polygon and qa/qc it
   * @throws Exception
   *
   */
  private void process() throws Exception
  {
    Enumeration polys =  polyLineSet.keys();
    int t=0;

    while(polys.hasMoreElements())
    {
     
       Long polyid = (Long) polys.nextElement();
       long polyid_long = polyid.longValue();
       ArrayList lines = (ArrayList) polyLineSet.get(polyid);
       if (lines.size() ==0 )
       {
         throw new Exception("polygon has no edges");
       }
         Polygonizer polyizer = new Polygonizer();
         polyizer.add(lines);
         Collection builtpolys = polyizer.getPolygons();
        
         if (polyizer.getCutEdges().size() != 0)
          {
           throw new Exception("polygon has cut edges");
        }
         if (polyizer.getDangles().size() != 0)
          {
           throw new Exception("polygon has dandgle edges");
        }
         if (polyizer.getInvalidRingLines().size() != 0)
          {
           throw new Exception("polygon has invalid edges");
         //  System.out.println("poly has invalid edges "+polyid_long);
        }
        
         //validate the polygons
         Geometry finalPolygon = null;
         Iterator it = builtpolys.iterator();
         ArrayList seenPolyIDs = new ArrayList();
         while (it.hasNext())
         {
           Polygon p = (Polygon) it.next();
           //each polygon must contain exactly one PIP
           //each PIP must be contained by at most one polygon!
          
           long polyidPIP;
          
           try{
          
             polyidPIP = getPIP(p,polyid_long, 0);
           }
           catch(Exception e)
        {
             if (!allowHolesWithNoPIP)
             {
               throw e; // rethrow it -- stop processing
             }
             //otherwise, this might be okay.
             polyidPIP = -666; // fake -- we pretend its a 'real' hole (ie. one with a pip in it)
             System.out.println("found a polygon without a PIP!  Try adding a PIP at "+p.getInteriorPoint() + " module = "+MODULE);
        }
//           if (seenPolyIDs.contains( new Long(polyidPIP) ))
//           {
//            //throw new Exception( "poly has 2 pips - dont think we're looking for it!");
//             // this is actually okay - you can have holes-inside holes! or holes touching each other
//           }
//           seenPolyIDs.add ( new Long(polyidPIP) );
           if (polyidPIP == polyid_long)
           {
             if (finalPolygon != null )
             {
                     throw new Exception( "poly has 2 pips - that we're looking for!");
               }
             //this is ours
             finalPolygon = p;
             finalPolygon.setUserData( polyid);
           }
         }
         if (finalPolygon == null)
         {
           throw new Exception("couldnt find a pip for a main polygon - "+polyid.longValue());
         }
      finishedPolys.put(polyid,finalPolygon);
      finalPolygon = null;
         t++;
        
    }
   
  }

  /**
   *   given a polyon, find the pip for it.
   * @param p
   * @param pointsTouchingOuterRightCountAsInside  NEVER CALL WITH THIS TRUE!
   * @return
   * @throws Exception
   */
  private long getPIP(Polygon p,long mainPolyID, int pointsTouchingOuterRightCountAsInside) throws Exception
  {
    //pointsTouchingOuterRightCountAsInside -- I noticed that there are points in the dataset that are touching
    // the outside edge of a polygon.  This only happens for very small polygons - when then numerical precision of the
    // outside edge's points arent really big enough to discern a point inside.
    //  this happens when the area is <10E-11
   
    // cases -- one pip & its the mainPolyID --> okay (found poly)
    //          1+ pip and none of them are mainpolyID (its a whole)
    //          0 --> exception (no point in this poly)
      List l =  PIP.query(p.getEnvelopeInternal());
      Iterator it = l.iterator();
     
      boolean determinedThisIsMainPolygon = false;
    
      boolean found=false;
      long foundID = 0;
       while (it.hasNext())
       {
          Point point = (Point) it.next();
          boolean inside = p.contains(point);
          if (pointsTouchingOuterRightCountAsInside ==1)
          {
                inside |= p.touches(point);
          }
          if (pointsTouchingOuterRightCountAsInside ==2)
          {
              inside |= p.distance(point) < 1E-8;
              System.out.println("distance = "+p.distance(point));
        }
          if (inside)
          {
            foundID = ( (Long) point.getUserData()).longValue()//Polyid for this PIP.
            if (determinedThisIsMainPolygon)
            {
              //bad -- this is the 2nd pip in a "main polygon"
            throw new Exception( "found a 2nd PIP in a main polygon "+mainPolyID+" and "+foundID);
            }
             determinedThisIsMainPolygon |= (foundID == mainPolyID);
             found = true;
          }
       }
       if (!found)
       {
              if (p.getArea() < 5E-9)
              {
                // its really small - we re-try allowing touching to mean inside
                if (pointsTouchingOuterRightCountAsInside ==0) // dont recurse indefinatly!
                {
                  System.out.println("small polygon with no PIP - retrying with pointsTouchingOuterRightCountAsInside=1 - "+mainPolyID +" area="+p.getArea());
                  return getPIP( p, mainPolyID, 1);
                }
                if (pointsTouchingOuterRightCountAsInside ==1) // dont recurse indefinatly!
                {
                    System.out.println("small polygon with no PIP - retrying with pointsTouchingOuterRightCountAsInside=2 - "+mainPolyID +" area="+p.getArea());
                return getPIP( p, mainPolyID, 2);
                }
              }
             
               throw new Exception( "poly has 0 pips! area="+p.getArea());
     }
        return foundID;
  }
}
TOP

Related Classes of tigerpoly.TigerPoly

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.