// ********************************************************************** // // // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/vpf/VMAP2Shape.java,v $ // $RCSfile: VMAP2Shape.java,v $ // $Revision: 1.3 $ // $Date: 2004/10/14 18:06:09 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.vpf; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import com.bbn.openmap.LatLonPoint; import com.bbn.openmap.MoreMath; import com.bbn.openmap.dataAccess.shape.EsriShapeExport; import com.bbn.openmap.layer.shape.ShapeFile; import com.bbn.openmap.layer.shape.ShapeUtils; import com.bbn.openmap.layer.util.LayerUtils; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.omGraphics.OMPoly; import com.bbn.openmap.omGraphics.SinkGraphic; import com.bbn.openmap.proj.DrawUtil; import com.bbn.openmap.proj.ProjMath; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.PropUtils; /** * Convert NIMA VMAP geospatial data into ESRI shapefile format. */ public class VMAP2Shape { protected final static int DEFAULT_SCALE = 30000000; protected static String vmaptype = "bnd"; protected static String propsFileName = System.getProperty("user.home") + System.getProperty("file.separator") + "openmap.properties"; protected static String prefix = "vmapref"; protected static boolean doThinning = false; protected static float fan_eps = 0.1f; protected static float zero_eps = 0.0001f; protected static float threshold = 0.5f; protected static int scale = DEFAULT_SCALE; protected LibrarySelectionTable lst; protected transient LayerGraphicWarehouseSupport warehouse; public VMAP2Shape() {} public void writeShapeFile(String shapeFileName, OMGraphicList graphics) { try { System.out.println("Performing thin on " +graphics +" with " +getRecursiveGraphicCount(graphics) +" total graphics"); graphics = thin(graphics); // recursive method to thin the data // save graphics System.out.println("Exporting " +getRecursiveGraphicCount(graphics) + " graphics to " +shapeFileName); EsriShapeExport ese = new EsriShapeExport(graphics, (Projection)null, shapeFileName); ese.export(); } catch (Exception x) { x.printStackTrace(); } } /* gets a count of the graphics in the list */ protected final static int getRecursiveGraphicCount(OMGraphicList graphics){ int count = 0; counting: for (int i = 0; i < graphics.size(); i++) { OMGraphic omg = graphics.getOMGraphicAt(i); if(omg instanceof OMGraphicList){ count += getRecursiveGraphicCount((OMGraphicList)omg); } else count++; } return count; } /* method to perform thinning of data in specified OMGraphicList */ protected final OMGraphicList thin(OMGraphicList graphics){ OMGraphicList saveGraphics = new OMGraphicList(); int nGraphics = graphics.size(); System.out.println(nGraphics +" candidate(s) for thinning"); int dropCount = 0; // // step through each item in the list thinning: for (int i = 0; i < nGraphics; i++) { OMGraphic omg = graphics.getOMGraphicAt(i); if(omg instanceof OMGraphicList){ // recurse omg = thin((OMGraphicList)omg); } else if(omg instanceof OMPoly && omg.getRenderType() == OMGraphic.RENDERTYPE_LATLON) { if (doThinning && maybeThrowAwayPoly((OMPoly) omg)) { // throw this OMPoly away by skipping to the next dropCount++; continue thinning; } } else System.out.println(omg +" " +omg.getRenderType()); saveGraphics.addOMGraphic(omg); } System.out.println("Thinned (dropped) " +dropCount +" graphics"); return joinCommonLines(saveGraphics); } // iterates through graphic list finding non-connected polylines. // iterates over these to find lines with common endpoints and // joining them. protected static OMGraphicList joinCommonLines(OMGraphicList list) { int size = list.size(); int len1, len2; float lat1, lon1, lat2, lon2; OMGraphic obj; OMGraphicList newGraphics = new OMGraphicList(); OMGraphicList plineGraphics = new OMGraphicList(); // check for non-connected polylines System.out.println("finding polylines..."); for (int i = 0; i < size; i++) { obj = list.getOMGraphicAt(i); if ((obj instanceof OMPoly) && !((OMPoly) obj).isPolygon()) { plineGraphics.addOMGraphic(obj); } else { newGraphics.addOMGraphic(obj); } } // iterate through the polylines and join lines with common // endpoints size = plineGraphics.size(); OMPoly poly1, poly2; float[] rads1, rads2, radians; System.out.println("maybe joining " + size + " polylines..."); // nasty!: > O(n^2) for (int i = 0; i < size; i++) { if (i % 500 == 0) { System.out.println("checking pline i=" + i); } for (int j = 0; j < size; j++) { if (i == j) { continue; } obj = plineGraphics.getOMGraphicAt(i); if (obj instanceof SinkGraphic) { continue; } poly1 = (OMPoly) obj; rads1 = poly1.getLatLonArray(); len1 = rads1.length; lat1 = ProjMath.radToDeg(rads1[len1 - 2]); lon1 = ProjMath.radToDeg(rads1[len1 - 1]); obj = plineGraphics.getOMGraphicAt(j); if (obj instanceof SinkGraphic) { continue; } poly2 = (OMPoly) obj; rads2 = poly2.getLatLonArray(); len2 = rads2.length; lat2 = ProjMath.radToDeg(rads2[0]); lon2 = ProjMath.radToDeg(rads2[1]); if (MoreMath.approximately_equal(lat1, lat2, zero_eps) && MoreMath.approximately_equal(lon1, lon2, zero_eps)) { // System.out.println("joining..."); radians = new float[len1 + len2 - 2]; System.arraycopy(rads1, 0, radians, 0, len1); System.arraycopy(rads2, 0, radians, len1 - 2, len2); poly1.setLocation(radians, OMGraphic.RADIANS); plineGraphics.setOMGraphicAt(SinkGraphic.getSharedInstance(), j); j = -1;//redo search } } } // add the joined lines back to the data set size = plineGraphics.size(); for (int i = 0; i < size; i++) { obj = plineGraphics.getOMGraphicAt(i); if (obj instanceof OMPoly) { newGraphics.addOMGraphic(obj); } } return newGraphics; } // traverse array and coalesce adjacent points which are the same public static float[] coalesce_points(float[] radians, float eps, boolean ispolyg) { int write = 2; int len = radians.length; for (int i = write - 2, j = write; j < len; j += 2) { float lat1 = ProjMath.radToDeg(radians[i]); float lon1 = ProjMath.radToDeg(radians[i + 1]); float lat2 = ProjMath.radToDeg(radians[j]); float lon2 = ProjMath.radToDeg(radians[j + 1]); if (MoreMath.approximately_equal(lat1, lat2, eps) && MoreMath.approximately_equal(lon1, lon2, eps)) { continue; } i = write; radians[write++] = radians[j]; radians[write++] = radians[j + 1]; } // check for mid-phase line if (ispolyg && (write == 6) && MoreMath.approximately_equal(radians[0], radians[4], eps) && MoreMath.approximately_equal(radians[1], radians[5], eps)) { write -= 2;//eliminate wrapped vertex } float[] newrads = new float[write]; System.arraycopy(radians, 0, newrads, 0, write); return newrads; } // return true if we should throw away the poly protected boolean maybeThrowAwayPoly(OMPoly poly) { float[] radians = poly.getLatLonArray(); float lat, lon, thresh = ProjMath.degToRad(threshold); radians = coalesce_points(radians, 0.0001f, poly.isPolygon()); poly.setLocation(radians, OMGraphic.RADIANS);//install new if (radians.length < 4) { return true;//throw away } if (poly.isPolygon() && (radians.length < 6)) { return true; } int len = radians.length; float d; for (int i = 0; i < len; i += 2) { // test for proximity to 1-degree marks. this hopefully // avoids the problem of throwing away tiled slivers. // (don't throw away poly) lat = ProjMath.radToDeg(radians[i]); lon = ProjMath.radToDeg(radians[i + 1]); if (MoreMath.approximately_equal(lat, (float) (Math.round(lat)), zero_eps)) { return false; } if (MoreMath.approximately_equal(lon, (float) (Math.round(lon)), zero_eps)) { return false; } // check to see if all points fit within a certain // threshold. this should eliminate small islands and // countries like Luxembourg. sorry. for (int j = i + 2; j < radians.length; j += 2) { d = DrawUtil.distance(radians[i], radians[i + 1], radians[j], radians[j + 1]); // outside threshold, don't throw away if (!MoreMath.approximately_equal(d, 0f, thresh)) { return false; } } } if (poly.isPolygon()) { return true;//throw away } // throw away polyline if it's connected (island) return (MoreMath.approximately_equal(ProjMath.radToDeg(radians[0]), ProjMath.radToDeg(radians[radians.length - 2]), zero_eps) && MoreMath.approximately_equal(ProjMath.radToDeg(radians[1]), ProjMath.radToDeg(radians[radians.length - 1]), zero_eps)); } protected Properties loadProperties() { Properties props = new Properties(); try { props.load(new FileInputStream(propsFileName)); } catch (IOException e) { e.printStackTrace(); System.exit(1); } return props; } protected void setProperties(String prefix, Properties props) { String realPrefix = PropUtils.getScopedPropertyPrefix(prefix); String[] paths = LayerUtils.initPathsFromProperties(props, realPrefix + VPFLayer.pathProperty); String defaultProperty = props.getProperty(realPrefix + VPFLayer.defaultLayerProperty); if (defaultProperty != null) { System.out.println("defaultProperty=" + defaultProperty); realPrefix = defaultProperty + "."; props = VPFLayer.getDefaultProperties(); } String coverage = props.getProperty(realPrefix + VPFLayer.coverageTypeProperty); if (coverage != null) { vmaptype = coverage; System.out.println("vmaptype=" + vmaptype); } initLST(paths); if (lst.getDatabaseName().equals("DCW")) { System.out.println("creating VPFLayerDCWWarehouse"); warehouse = new VPFLayerDCWWarehouse(); } else { System.out.println("creating VPFLayerGraphicWarehouse"); warehouse = new VPFLayerGraphicWarehouse(); } LayerGraphicWarehouseSupport.setDoThinning(doThinning); LayerGraphicWarehouseSupport.setFanEpsilon(fan_eps); warehouse.setProperties(realPrefix, props); } protected void initLST(String[] paths) { try { if (lst == null) { lst = new LibrarySelectionTable(paths); } } catch (com.bbn.openmap.io.FormatException f) { throw new java.lang.IllegalArgumentException(f.getMessage()); } } public OMGraphicList getRectangle() { //int scale = 20000000; int width = 1600; int height = 1200; //int scale = 30000000; //int width = 640; //int height = 480; LatLonPoint upperLeft = new LatLonPoint(90.0f, -180.0f); LatLonPoint lowerRight = new LatLonPoint(-90.0f, 180.0f); warehouse.clear(); System.out.println("VMAP2Shape.getRectangle() calling drawTile, scale=" +scale +", boundaries=" + upperLeft + lowerRight); long start = System.currentTimeMillis(); lst.setCutoffScale(scale +10); lst.drawTile(scale, width, height, vmaptype, warehouse, upperLeft, lowerRight); long stop = System.currentTimeMillis(); System.out.println("VMAP2Shape.getRectangle(): read time: " + ((stop - start) / 1000d) + " seconds"); return warehouse.getGraphics(); } public static void usage() { System.out.println("Usage: java VMAP2Shape [args] "); System.out.println("Arguments:"); System.out.println("\t-props path to properties file"); System.out.println(" default: " + propsFileName); System.out.println("\t-prefix vmap properties prefix"); System.out.println(" default: " + prefix); System.out.println("\t-scale map scale (1:X)"); System.out.println(" default: " +DEFAULT_SCALE); System.out.println("\t-thin do thinning"); System.out.println(" default eps=" + fan_eps + " thresh=" + threshold); System.exit(1); } public static void main(String args[]) { if ((args.length == 0) || ((args.length == 1) && (args[0].startsWith("-")))) { usage(); } com.bbn.openmap.util.Debug.init(System.getProperties()); com.bbn.openmap.util.Debug.put("debug.vpf"); com.bbn.openmap.util.Debug.put("vpf"); VMAP2Shape c = new VMAP2Shape(); for (int i = 0; i < args.length - 1; i++) { if (args[i].equalsIgnoreCase("-props")) { propsFileName = args[++i]; } else if (args[i].equalsIgnoreCase("-prefix")) { prefix = args[++i]; } else if (args[i].equalsIgnoreCase("-scale")) { try{ scale = Integer.parseInt(args[++i]); } catch(Exception x){} // NumberFormatException } else if (args[i].equalsIgnoreCase("-thin")) { doThinning = true; fan_eps = Float.valueOf(args[++i]).floatValue(); threshold = Float.valueOf(args[++i]).floatValue(); } else { usage(); } } c.setProperties(prefix, c.loadProperties()); c.writeShapeFile(args[args.length - 1], c.getRectangle()); } }