package com.bbn.openmap.layer.learn; import java.awt.Color; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import com.bbn.openmap.layer.OMGraphicHandlerLayer; import com.bbn.openmap.layer.policy.BufferedImageRenderPolicy; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.omGraphics.OMPoint; import com.bbn.openmap.omGraphics.OMText; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.DataBounds; /** * This layer demonstrates how to use the projection to update the OMGraphics a * Layer is managing when the map view changes. * * If you aren't sure how Layers manage OMGraphics, check out the BasicLayer * first. */ public class ProjectionResponseLayer extends OMGraphicHandlerLayer { /** * The empty constructor is necessary for any layer being created using the * openmap.properties file, via the openmap.layers property. This method * needs to be public, too. Don't try to do too much in the constructor - * remember, this code gets executed whether the user uses the layer or not. * Performance-wise, it's better to do most initialization the first time * the layer is made part of the map. You can test for that in the prepare() * method, by testing whether the OMGraphicList for the layer is null or * not. * * @see #prepare */ public ProjectionResponseLayer() { // Sets the name of the layer that is visible in the GUI. Can also be // set with properties with the 'prettyName' property. setName("Projection Response Layer"); // This is how to set the ProjectionChangePolicy, which // dictates how the layer behaves when a new projection is // received. The ListResetPCPolicy is a policy that clears out the old // OMGraphicList when the projection changes so that old data doesn't // get redrawn on the map before this layer has the chance to update the // list it renders. // Remember, you don't have control over when paint() gets called on // this layer. All layers work independently, so if other layers finish // handling the projection change before this one, they will request to // be painted. This will cause paint() to be called on all // layers that are on/visible/part of the map. If the layer is still in // the prepare() method when those paint() calls are made, you want to // make sure your layer doesn't render anything if your layer's list is // from the old projection. setProjectionChangePolicy(new com.bbn.openmap.layer.policy.ListResetPCPolicy(this)); // Improves performance setRenderPolicy(new BufferedImageRenderPolicy()); } /** * This is an important Layer method to override. The prepare method gets * called when the layer is added to the map, or when the map projection * changes. We need to make sure the OMGraphicList returned from this method * is what we want painted on the map. The OMGraphics need to be generated * with the current projection. */ public synchronized OMGraphicList prepare() { // We're going to create a new OMGraphicList to return from this // projection change. The ListRestPCPolicy has already taken care of // removing the OMGraphics from the current list (if it exists) so we // can just ignore that for now. OMGraphicList list = new OMGraphicList(); Projection proj = getProjection(); // Just a safety check in case someone calls prepare without ever adding // it to the map. if (proj == null) { return list; } Point2D upperLeft = proj.getUpperLeft(); Point2D lowerRight = proj.getLowerRight(); /** * Now, upperLeft and lowerRight are the coordinate bounds covered by * the map. You can use those bounds to filter data. * * One other thing. OpenMap projections can only cover a maximum of 360 * degrees. So to test for the date line, you can check the longitudes * of the corners. If the x value of the left side is greater than the x * value of the right side, the map is covering the date line. */ if (upperLeft.getX() > lowerRight.getX()) { // crossing the date line. Depending on your data source, you may // need to make two queries for your data, one for the left side of // date line, one for the right side. If you want to see what // happens without this check, go ahead and comment these two lines // out. // Make query for map on left side of date line getPoints(new DataBounds(upperLeft, new Point2D.Double(180, lowerRight.getY())), list, proj); // Make query for map on right side if date line getPoints(new DataBounds(new Point2D.Double(-180, upperLeft.getY()), lowerRight), list, proj); } else { getPoints(new DataBounds(upperLeft, lowerRight), list, proj); } // Let's add a little statement in the lower left corner of the map // describing the number of OMPoints created and drawn. OMText statement = new OMText(10, proj.getHeight() - 10, getName() + " displaying " + list.size() + "/" + dataSource.size() + " points", OMText.JUSTIFY_LEFT); // Just to add a little background behind the letters. statement.setFillPaint(Color.gray); // Have to generate the OMText, too. statement.generate(proj); // Add it to the front of the list, so it's on top. list.add(0, statement); return list; } /** Some make-believe source of data. */ protected List<Point2D> dataSource; /** * Using this method to look at data source and create OMGraphics based on * DataBounds, and render them differently depending on where they are. * * @param dataBounds the bounds of the map projection. * @param retList the list to add new OMGraphics to. * @param proj the projection to use for generating OMGraphics. If we pass * the projection to the point where the OMGraphics are created and * generate them at creation time, we save ourselves another loop * through the OMGraphicList to generate them later. */ protected void getPoints(DataBounds dataBounds, OMGraphicList retList, Projection proj) { if (dataSource == null) { // For lack of having a data source, lets just create one on the // fly. dataSource = initSource(); } for (Point2D point : dataSource) { if (dataBounds.contains(point)) { OMPoint newPoint = new OMPoint(point.getY(), point.getX()); // Color western points green, eastern points yellow newPoint.setFillPaint(point.getX() < 0 ? Color.green : Color.yellow); newPoint.generate(proj); retList.add(newPoint); } } } /** * Just creating a set of Points to use as data source. * * @return List of Point2D objects in a grid. */ protected List<Point2D> initSource() { List<Point2D> source = new ArrayList<Point2D>(); for (double y = -85; y <= 85; y++) { for (double x = -180; x < 180; x++) { source.add(new Point2D.Double(x, y)); } } logger.fine("created source with " + source.size() + " points"); return source; } }