/** * This main function basically reads in the data sources (the shape file and the csv information * file, and creates a serialized graphics file that will act like a cache later. */ public static void main(String[] argv) { String propertiesFile = null; String prefix = null; String outputFile = null; Debug.init(); if (argv.length < 6) printUsage(); for (int i = 0; i < argv.length; i++) { if (argv[i].equalsIgnoreCase("-props")) { propertiesFile = argv[++i]; } else if (argv[i].equalsIgnoreCase("-prefix")) { prefix = argv[++i]; } else if (argv[i].equalsIgnoreCase("-file")) { outputFile = argv[++i]; } } if (propertiesFile == null || prefix == null || outputFile == null) { printUsage(); } try { Properties properties = new Properties(); // Read in the properties. URL propertiesURL = new URL(propertiesFile); InputStream is = propertiesURL.openStream(); properties.load(is); // Let's make a file ShapeLayer sl = new ShapeLayer(); sl.setProperties(prefix, properties); AreaHandler ah = new AreaHandler(sl.getSpatialIndex(), sl.getDrawingAttributes()); // Set the properties in the handler. ah.setProperties(prefix, properties); // Write the saved graphics. ah.getGraphics().writeGraphics(outputFile); } catch (java.net.MalformedURLException murle) { Debug.error("Bad URL for properties file : " + propertiesFile); printUsage(); } catch (java.io.IOException ioe) { Debug.error("IOException creating cached graphics file: " + outputFile); printUsage(); } }
/** * Method to set the properties in the PropertyConsumer. The prefix is a string that should be * prepended to each property key (in addition to a separating '.') in order for the * PropertyConsumer to uniquely identify properties meant for it, in the midst of Properties meant * for several objects. * * @param prefix a String used by the PropertyConsumer to prepend to each property value it wants * to look up - setList.getProperty(prefix.propertyKey). If the prefix had already been set, * then the prefix passed in should replace that previous value. * @param setList a Properties object that the PropertyConsumer can use to retrieve expected * properties it can use for configuration. */ public void setProperties(String prefix, Properties setList) { super.setProperties(prefix, setList); prefix = PropUtils.getScopedPropertyPrefix(prefix); wmsServer = setList.getProperty(prefix + WMSServerProperty); if (wmsServer == null) { Debug.error("WMSPlugIn needs a WMS server."); } queryHeader = wmsServer; setImageFormat(setList.getProperty(prefix + ImageFormatProperty, getImageFormat())); setTransparent(setList.getProperty(prefix + TransparentProperty, getTransparent())); setBackgroundColor(setList.getProperty(prefix + BackgroundColorProperty, getBackgroundColor())); setWmsVersion(setList.getProperty(prefix + WMSVersionProperty, getWmsVersion())); layers = setList.getProperty(prefix + LayersProperty); styles = setList.getProperty(prefix + StylesProperty); /** Include for vendor specific parameters */ setVendorSpecificNames( setList.getProperty(prefix + VendorSpecificNamesProperty, getVendorSpecificNames())); setVendorSpecificValues( setList.getProperty(prefix + VendorSpecificValuesProperty, getVendorSpecificValues())); } // end setProperties
/** Write an OMPoint to the Link. */ public static void write(OMPoint point, Link link, LinkProperties props) throws IOException { props.setProperty(LinkPoint.LPC_POINT_OVAL, point.isOval() ? "true" : "false"); switch (point.getRenderType()) { case OMPoint.RENDERTYPE_LATLON: LinkPoint.write( (float) point.getLat(), (float) point.getLon(), point.getRadius(), props, link.dos); break; case OMPoint.RENDERTYPE_XY: LinkPoint.write(point.getX(), point.getY(), point.getRadius(), props, link.dos); break; case OMPoint.RENDERTYPE_OFFSET: LinkPoint.write( (float) point.getLat(), (float) point.getLon(), point.getX(), point.getY(), point.getRadius(), props, link.dos); break; default: Debug.error("LinkPoint.write: point rendertype unknown."); } }
public void actionPerformed(ActionEvent ae) { if (map != null) { Proj proj = (Proj) map.getProjection(); DataBounds bounds = provider.getDataBounds(); if (bounds != null) { java.awt.geom.Point2D center = bounds.getCenter(); if (center != null) { proj.setCenter((float) center.getY(), (float) center.getX()); LatLonPoint llp1 = new LatLonPoint(bounds.getMax().getY(), bounds.getMin().getX()); LatLonPoint llp2 = new LatLonPoint(bounds.getMin().getY(), bounds.getMax().getX()); // 1.1 buffers the edges for viewing a little, a // little zommed out. proj.setScale(ProjMath.getScale(llp1, llp2, proj) * 1.1f); map.setProjection(proj); } } else { String complaint = "Can't move map over data: " + provider.getName() + " isn't ready. Add to map?"; if (infoDelegator != null) { infoDelegator.displayMessage("Go Over Data", complaint); } else { Debug.error(complaint); } } } }
/** Get all the graphics from the shapefile, colored appropriately. */ public OMGraphicList getGraphics() { if (omgraphics == null) { omgraphics = new OMGraphicList(); try { spatialIndex.getOMGraphics( -180, -90, 180, 90, omgraphics, drawingAttributes, (Projection) null, coordTransform); updateDrawingParameters(omgraphics); } catch (IOException ioe) { Debug.error(ioe.getMessage()); } catch (FormatException fe) { Debug.error(fe.getMessage()); } } return omgraphics; }
/** * OK, we can't assume that we are assigning a string as a key, you might want to key in on a * specific attribute that is a number, like the country coloring code that ESRI has in the * country file. We're going to assume that if the number has an integer value, it shouldn't have * decimal places. That is, a 1.0 will be truncated to 1, because that makes more sense in a data * file where you are using a key as a factor. If the double value doesn't match the integer * value, though, we'll assume that's what was meant and leave it alone. * * <p> */ protected String createStringFromKeyObject(Object keyObj) { String key = null; if (keyObj instanceof String) { key = ((String) keyObj).toUpperCase().intern(); } else if (keyObj instanceof Number) { Number keyNum = (Number) keyObj; if (keyNum.doubleValue() == (double) keyNum.intValue()) { // Strips off empty decimal places, for sure key = Integer.toString(keyNum.intValue()).intern(); } else { key = Double.toString(keyNum.doubleValue()).intern(); } } else { try { key = keyObj.toString().toUpperCase().intern(); } catch (Exception e) { Debug.error("AreaHandler.createStringFromKeyObject: bad key object:" + keyObj); } } return key; }
/** * Find a PoliticalArea named by the search key. If the shapefile is large, the first query will * take a little extra time on the first query to read in the files. * * @param area_key the lookup key, of which the index for the column was designated in the * properties file. */ public PoliticalArea findPoliticalArea(String area_key) { // Right now, this method gathers all the graphics in the // shape file, groups them, and then returns the PoliticalArea // for the key. In the future, it would be nice to have the // option to actually search through the data file, find the // indexes of the graphics that go to the area, and assemble a // temporary list to pass back. if (politicalAreas == null) { Debug.message("areas", "AreaHandler: initializing graphic attributes"); initialize(originalPrefix, originalProperties); if (omgraphics == null) { omgraphics = getGraphics(); if (dbfModel != null) loadDbfModelIntoGraphics(omgraphics); else infoFile.loadIntoGraphics(omgraphics); } politicalAreas = determinePoliticalAreas(omgraphics); Debug.message("areas", "AreaHandler: completed initialization"); } if (politicalAreas != null) { String key = area_key.toUpperCase().intern(); // Just to // be sure. return (PoliticalArea) politicalAreas.get(key); } else { Debug.error( "AreaHandler: initialization failed for " + originalPrefix + "\n\tNo data will be displayed"); return null; } }
/** * Prepare the spline for rendering. * * @see com.bbn.openmap.omGraphics.OMGeometry#generate(Projection) * @param proj Projection * @return true if generate was successful */ public boolean generate(Projection proj) { setNeedToRegenerate(true); if (proj == null) { Debug.message("omspline", "OMSpline: null projection in generate!"); return false; } NatCubicSpline spline = isGeometryClosed() ? natCubicClosed : natCubic; // HACK : should use something else than nsegs spline.setSteps(nsegs); float[][] splinePoints; switch (renderType) { case RENDERTYPE_XY: if (xs == null) { Debug.message("omspline", "OMSpline x/y rendertype null coordinates"); setNeedToRegenerate(true); return false; } splinePoints = spline.calc(xs, ys); xpoints = new float[1][0]; xpoints[0] = splinePoints[0]; ypoints = new float[1][0]; ypoints[0] = splinePoints[1]; break; case RENDERTYPE_OFFSET: if (xs == null) { Debug.message("omspline", "OMSpline offset rendertype null coordinates"); setNeedToRegenerate(true); return false; } int npts = xs.length; float[] _x = new float[npts]; float[] _y = new float[npts]; // forward project the radian point Point origin = new Point(); if (proj instanceof GeoProj) { ((GeoProj) proj).forward(lat, lon, origin, true); // radians } else { proj.forward(Math.toDegrees(lat), Math.toDegrees(lon), origin); } if (coordMode == COORDMODE_ORIGIN) { for (int i = 0; i < npts; i++) { _x[i] = (float) (xs[i] + origin.getX()); _y[i] = (float) (ys[i] + origin.getY()); } } else { // CModePrevious offset deltas _x[0] = xs[0] + origin.x; _y[0] = ys[0] + origin.y; for (int i = 1; i < npts; i++) { _x[i] = xs[i] + _x[i - 1]; _y[i] = ys[i] + _y[i - 1]; } } splinePoints = spline.calc(_x, _y); xpoints = new float[1][0]; xpoints[0] = splinePoints[0]; ypoints = new float[1][0]; ypoints[0] = splinePoints[1]; break; case RENDERTYPE_LATLON: if (rawllpts == null) { Debug.message("omspline", "OMSpline latlon rendertype null coordinates"); setNeedToRegenerate(true); return false; } // spline creation ; precision 1e-8 rad = 0.002" double[] splinellpts = spline.calc(rawllpts, 1e-8f); // polygon/polyline project the polygon/polyline. // Vertices should already be in radians. ArrayList<float[]> vector; if (proj instanceof GeoProj) { vector = ((GeoProj) proj).forwardPoly(splinellpts, lineType, nsegs, isPolygon); } else { vector = proj.forwardPoly(rawllpts, isPolygon); } int size = vector.size(); xpoints = new float[(int) (size / 2)][0]; ypoints = new float[xpoints.length][0]; for (int i = 0, j = 0; i < size; i += 2, j++) { xpoints[j] = vector.get(i); ypoints[j] = vector.get(i + 1); } if (!doShapes && size > 1) { setNeedToRegenerate(false); initLabelingDuringGenerate(); setLabelLocation(xpoints[0], ypoints[0]); return true; } break; case RENDERTYPE_UNKNOWN: Debug.error("OMSpline.generate: invalid RenderType"); return false; } setNeedToRegenerate(false); setShape(createShape()); setLabelLocation(getShape(), proj); return true; }
/** * DeterminePoliticalAreas goes over a list of omgraphics, and spits out a hashtable that holds * PoliticalArea objects for every area key. When an ID is found in the graphics, it is checked in * the hashtable for like graphics, and added to that PoliticalArea if found. If not found, a new * PoliticalArea is created and placed in the Hashtable. This will duplicate graphics if you call * it more than once for the same graphic list. * * @param graphicList the list of graphics. The top level graphic entries on the list represent * areas. */ public Hashtable determinePoliticalAreas(OMGraphicList graphicList, Hashtable poli_areas) { // Simple case. No graphics means an empty list of regions. String name = null; String key = null; if (graphicList != null) { int size = graphicList.size(); for (int i = 0; i < size; i++) { OMGraphic graphic = graphicList.getOMGraphicAt(i); // below should be a vector like [ "Massachusetts", // "MA" ]; Object obj = graphic.getAttribute(ShapeConstants.SHAPE_DBF_INFO_ATTRIBUTE); if (obj == null) { if (Debug.debugging("areas")) { Debug.error("AreaHandler: No attributes for graphic #" + i); } continue; } if (obj instanceof Vector) { Vector pair = (Vector) obj; name = (String) pair.elementAt(nameIndex); key = ((String) pair.elementAt(keyIndex)).toUpperCase().intern(); if (Debug.debugging("areas")) { Debug.output("AreaHandler: looking at " + name + ", " + key); } } else if (obj instanceof String) { // Assume that the key is stored here, I guess. key = (String) obj; if (Debug.debugging("areas")) { Debug.output("AreaHandler: String app object, looking at " + key); } } else { if (Debug.debugging("areas")) { Debug.output("AreaHandler: Unidentified app object type " + obj); } } // Get the political area object for this keyiation. PoliticalArea area = (PoliticalArea) poli_areas.get(key); if (area == null) { // key is not in table area = new PoliticalArea(name, key); poli_areas.put(key, area); // add it to the table // AreaDrawParams adp = // (AreaDrawParams)drawingParams.get(key); // if (adp != null) { // area.setDrawingAttributes(adp.drawingAttributes); // } } // Add the graphic to the list for this political // area. area.addGraphic(graphic); } if (Debug.debugging("areas")) { Debug.output( "AreaHandler: Finished determinePoliticalAreas: " + poli_areas.size() + " areas defined."); } } return poli_areas; }
/** * Go through the properties, loading the shapefile, information file and attributes files, and * resolve how everything should be drawn. Might take awhile if the files are large. Called from * getRectangle, which is called when the AreaShapeLayer is added to the map. * * @param prefix property file entry header name * @param props the properties to look through. */ public void initialize(String prefix, Properties props) { if (props == null) { Debug.error( "AreaHandler: initialize received bad input:\n\tprefix: " + prefix + "\n\tproperties: " + (props == null ? "null" : "OK")); politicalAreas = null; return; } prefix = PropUtils.getScopedPropertyPrefix(prefix); politicalAreas = new Hashtable(); // OK, Get the graphics. We are not expecting that all the // graphics in the file are not too much to handle. Also, we // test for the serialized graphics file first, and if it // isn't designated, then look for a shapefile and spatial // index file to create an OMGraphicsList. String cacheFile = props.getProperty(prefix + CacheFileProperty); // First find the resource, if not, then try as a file-URL... try { cacheURL = PropUtils.getResourceOrFileOrURL(this, cacheFile); if (cacheURL != null) { omgraphics = readCachedGraphics(cacheURL); } else { // We'll use the spatial index set from the // ShapeLayer. // Now, get the attribute file String dbfFile = props.getProperty(prefix + dbfFileProperty); URL dbfFileURL = null; if (dbfFile != null) { dbfFileURL = PropUtils.getResourceOrFileOrURL(this, dbfFile); } if (dbfFileURL != null) { InputStream is = dbfFileURL.openStream(); dbfModel = new DbfTableModel(new DbfInputStream(is)); } if (dbfModel == null) { String csvFile = props.getProperty(prefix + csvFileProperty); URL infofileURL = null; if (csvFile != null) { infofileURL = PropUtils.getResourceOrFileOrURL(this, csvFile); } // Read them in. if (infofileURL != null) { infoFile = new CSVShapeInfoFile(csvFile); infoFile.setHeadersExist( PropUtils.booleanFromProperties(props, prefix + csvHeaderProperty, true)); infoFile.loadData(true); } } } } catch (java.net.MalformedURLException murle) { omgraphics = new OMGraphicList(); } catch (java.io.IOException ioe) { omgraphics = new OMGraphicList(); } catch (Exception exc) { omgraphics = new OMGraphicList(); } // This is handled properly yet. The PoliticalArea should be // updated to handle URLs for area points, and have different // icons for different areas. // String defaultPointImageURLString = // props.getProperty(prefix + pointImageURLProperty); // Now, match the attributes to the graphics. Find the // indexes of the name and the search key. Also figure out // which areas have special coloring needs. keyIndex = PropUtils.intFromProperties(props, prefix + keyIndexProperty, keyIndex); nameIndex = PropUtils.intFromProperties(props, prefix + nameIndexProperty, nameIndex); String areas = props.getProperty(prefix + areasProperty); if (areas == null) areas = ""; StringTokenizer tokenizer = new StringTokenizer(areas, " "); // All this uses the properties to set the individual colors // of any area String currentArea; while (tokenizer.hasMoreTokens()) { currentArea = tokenizer.nextToken(); PoliticalArea newParams = new PoliticalArea(currentArea); if (Debug.debugging("areas")) { Debug.output("AreaHandler: setting SPECIALIZED attributes for \"" + newParams.id + "\""); } areasItems.addElement(currentArea); newParams.drawingAttributes = new DrawingAttributes(prefix + areasProperty + "." + currentArea, props); politicalAreas.put(newParams.id.toUpperCase().intern(), newParams); } if (Debug.debugging("areas")) { Debug.output("AreaHandler: finished initialization"); } }
/** * Prepares the graphics for the layer. This is where the getRectangle() method call is made on * the dted. * * <p>Occasionally it is necessary to abort a prepare call. When this happens, the map will set * the cancel bit in the LayerThread, (the thread that is running the prepare). If this Layer * needs to do any cleanups during the abort, it should do so, but return out of the prepare asap. */ public synchronized OMGraphicList prepare() { if (isCancelled()) { Debug.message("dted", getName() + "|DTEDLayer.prepare(): aborted."); return null; } Projection projection = getProjection(); if (projection == null) { Debug.error("DTED Layer needs to be added to the MapBean before it can draw images!"); return new OMGraphicList(); } DTEDCacheManager cache = getCache(); if (!(projection instanceof EqualArc)) { // fireRequestInfoLine("DTED works faster with an Equal Arc projection (CADRG/LLXY)."); } Debug.message("basic", getName() + "|DTEDLayer.prepare(): doing it"); // Setting the OMGraphicsList for this layer. Remember, the // OMGraphicList is made up of OMGraphics, which are generated // (projected) when the graphics are added to the list. So, // after this call, the list is ready for painting. // call getRectangle(); if (Debug.debugging("dted")) { Debug.output( getName() + "|DTEDLayer.prepare(): " + "calling getRectangle " + " with projection: " + projection + " ul = " + projection.getUpperLeft() + " lr = " + projection.getLowerRight()); } OMGraphicList omGraphicList; if (projection.getScale() < maxScale) { omGraphicList = cache.getRectangle(projection); } else { fireRequestInfoLine(" The scale is too small for DTED viewing."); Debug.error( "DTEDLayer: scale (1:" + projection.getScale() + ") is smaller than minimum (1:" + maxScale + ") allowed."); omGraphicList = new OMGraphicList(); } // /////////////////// // safe quit int size = 0; if (omGraphicList != null) { size = omGraphicList.size(); Debug.message( "basic", getName() + "|DTEDLayer.prepare(): finished with " + size + " graphics"); // // Don't forget to project them. Since they are only // // being recalled if the projection has changed, then we // // need to force a reprojection of all of them because the // // screen position has changed. // omGraphicList.project(projection, true); } else { Debug.message("basic", getName() + "|DTEDLayer.prepare(): finished with null graphics list"); } return omGraphicList; }
/** * Given a couple of points representing a bounding box, find out what the scale should be in * order to make those points appear at the corners of the projection. * * @param ll1 the upper left coordinates of the bounding box. * @param ll2 the lower right coordinates of the bounding box. * @param point1 a Point2D reflecting a pixel spot on the projection that matches the ll1 * coordinate, the upper left corner of the area of interest. * @param point2 a Point2D reflecting a pixel spot on the projection that matches the ll2 * coordinate, usually the lower right corner of the area of interest. * @param recursiveCount a protective count to keep this method from getting in a recursive death * spiral. */ private float getScale( Point2D ll1, Point2D ll2, Point2D point1, Point2D point2, int recursiveCount) { try { double deltaDegrees; double pixPerDegree; int deltaPix; double ret; double dx = Math.abs(point2.getX() - point1.getX()); double dy = Math.abs(point2.getY() - point1.getY()); double nCenterLat = Math.min(ll1.getY(), ll2.getY()) + Math.abs(ll1.getY() - ll2.getY()) / 2; double nCenterLon = Math.min(ll1.getX(), ll2.getX()) + Math.abs(ll1.getX() - ll2.getX()) / 2; if (dx < dy) { double dlat = Math.abs(ll1.getX() - ll2.getY()); deltaDegrees = dlat; deltaPix = getHeight(); pixPerDegree = getScale() * getYPixConstant() / 90; } else { double dlon; double lat1, lon1, lon2; // point1 is to the right of point2. switch the // LatLonPoints so that ll1 is west (left) of ll2. if (point1.getX() > point2.getX()) { lat1 = ll1.getY(); lon1 = ll1.getX(); ll1.setLocation(ll2); // Remember for setLocation the order of args is reversed ll2.setLocation(lon1, lat1); } lon1 = ll1.getX(); lon2 = ll2.getX(); // allow for crossing dateline if (lon1 > lon2) { dlon = (180 - lon1) + (180 + lon2); } else { dlon = lon2 - lon1; } deltaDegrees = dlon; deltaPix = getWidth(); pixPerDegree = getPlanetPixelCircumference() / 360; } // The new scale... ret = pixPerDegree / (deltaPix / deltaDegrees); // OK, now given the new scale at the apparent new center // location, we need to test if the zone changes, because // if it does, the values don't work out right because the // pixel spacings are different. If the zones are // different, we need to recalculate the scale based on // the // new zone. CADRG newcadrg = new CADRG( new LatLonPoint.Double(nCenterLat, nCenterLon), (float) ret, getWidth(), getHeight()); // Use the recursiveCount to prevent extended recalls. A // couple rounds should suffice. if (newcadrg.getZone() != zone && recursiveCount < 2) { ret = newcadrg.getScale( ll1, ll2, newcadrg.forward(ll1), newcadrg.forward(ll2), recursiveCount + 1); } return (float) ret; } catch (NullPointerException npe) { Debug.error("ProjMath.getScale(): caught null pointer exception."); return Float.MAX_VALUE; } }
public void actionPerformed(ActionEvent ae) { Debug.message("saveimage", "SaveAsImageMenuItem: actionPerformed"); if (mapHandler == null) { Debug.output("SaveAsImageMenuItem: mapHandler = null, returning"); return; } MapBean mb = (MapBean) mapHandler.get("com.bbn.openmap.MapBean"); if (mb != null) { Debug.message("saveimage", "MapBean found, creating image"); try { while (true) { SaveAsImageFileChooser chooser = new SaveAsImageFileChooser(mb.getWidth(), mb.getHeight()); int returnVal = chooser.showSaveDialog(getParent()); if (returnVal == JFileChooser.APPROVE_OPTION) { String filename = chooser.getSelectedFile().getAbsolutePath(); if (formatter == null) { break; } filename = checkFileName(filename, formatter.getFormatLabel().toLowerCase()); if (filename == null) { // This is the reason for the while // loop, the name didn't really pass // muster, so we'll try again. continue; } int imageHeight = chooser.getImageHeight(); int imageWidth = chooser.getImageWidth(); byte[] imageBytes = formatter.getImageFromMapBean(mb, imageWidth, imageHeight); FileOutputStream binFile = new FileOutputStream(filename); binFile.write(imageBytes); binFile.close(); if (Debug.debugging("saveimage")) { com.bbn.openmap.proj.Projection proj = mb.getProjection(); Debug.output( "Created image at " + filename + "where projection covers " + proj.getUpperLeft() + " to " + proj.getLowerRight()); } break; } else if (returnVal == JFileChooser.CANCEL_OPTION) { break; } } } catch (IOException e) { Debug.error("SaveAsImageMenuItem: " + e); } } return; }