Homework 2
DUE: October 29, 2001
THIS SECTION CONTAINS INFORMATION POSTED AFTER ORIGINAL HANDOUT |
---|
0. Solutions from two groups: Gleyzer | Xing Xia
|
1. If you need to compute the distance between two points in earth coordinates, here is a paper TSPLIB95 that Xing Xia kindly brought to our attention: TSPLIB95 On page 7 of this paper, the distance formula is given.
|
2. You actually do not need to figure out on which side of the Tiger Line is a Tiger polygon that it bounds: this information is explicitly given in the Tiger files.
|
3. You cannot render non-convex polygons in OpenGL, but
you can use the GLU tessalation routines. This is found in chapter
11 of the OpenGL Programming Guide (Red Book).
|
4. Corrected an omission in Subdivision class: each
line needs to point to one of its half-edges.
|
We continue with visualization of 2-D TIGER data. The above website has all the resources you need, including tools to download TIGER files for any county in the country.
We want to do three things: (1) We want to generate a very large dataset for our visualization experimentations. (2) We want to gain some experience with the half-edge data structure for some basic processing. (3) We want to begin to experiment with LOD hierarchies.
I also want you to work in groups of 4 or more students, so that you can coordinate and discuss the assignment. As usual, do not do the next stage until you have done the earlier stages. Please note that there is NO competition among the groups - feel free to share information across groups.
We want to represent a map of some region. The map will be represented by an instance of the Subdivision class, based on the Half-Edge Data Structure. Each Subdivision instance has four lists: a list of vertices, edges, polygons and half-edges. Here ``edges'' actually means polygonal lines in the sense of TIGER Lines (abbrev. TLines). We want it to be as simple and efficient as possible, and hence avoid the use of private data and special methods for accessing them. Thus all the member variables in the Subdivision class are public. The following data structure should be used:
class Subdivision { public: /////////////////////////////////////////////////////// // General Subdivision Information /////////////////////////////////////////////////////// int ID; // unique ID for subdivision char * name; // Name or other information Point2 southWest; // south-west corner of bounding box Point2 northEast; // north-east corner of bounding box /////////////////////////////////////////////////////// // Vertex List /////////////////////////////////////////////////////// int numVertices; Point2 * Vertex; // Vertex[i] is a Point2 (from Hill's book) // These are the end points of Tiger Lines // Length of Vertex array is numVertices. /////////////////////////////////////////////////////// // Polygon List /////////////////////////////////////////////////////// int numPolygons; int * PolyHalfEdge; // PolyHalfEdge[i] points to ANY half-edge // that bounds the polygon int * PolyType; // 0=unused, 1=water, 2=land, 3=park, 4=landmark // Color them white/blue/yellow/green/pink. /////////////////////////////////////////////////////// // Line List (NOTE: "TLine" is short for TIGER Lines /////////////////////////////////////////////////////// int numLine; // Number of TLines int * LineLen; // LineLen[i] is the number of detailed // points in the i-th TLine Point2 * LineDetail[]; // LineDetail[i] is the list of detailed // points in the i-th TLine int * HalfEdge[]; // points to ANY one of the two // half-edges associated with this TLine int * LineType; // 0=isthmus, 1=highway, 2=local road, // 3=rail, 4=boundary, // 5=river or shoreline, etc /////////////////////////////////////////////////////// // Half-Edge List /////////////////////////////////////////////////////// int * HeEdge; // HeEdge[j] is the edge index // of the j-th half-edge int * HeTwin; // HeTwin[i] is index of the twin of the // j-th half-edge int * HeStartPt; // HeStartPt[j] is the (index of the) // start vertex of j-th half-edge int * HeEndPt; // HeEndPt[j] is the end point of j-th half-edge int * HeSucc; // HeSucc[j] and HePred[j] are the indices of int * HePred; // the successor/predecessor half-edges int * HePoly; // HePoly[j] is the polygon bounding the // j-th half-edge. CONVENTION: following // the HeSucc links will go CCW about // this polygon. }//class Subdivision
Now we derive a class from Subdivision class to represent the information in TIGER datasets:
class TigerSubdiv : public Subdivision { public: /////////////////////////////////////////////////////// // General County Information /////////////////////////////////////////////////////// // NOTE: ID may be the "FIPS" of the county /////////////////////////////////////////////////////// // Polygon List /////////////////////////////////////////////////////// int * PolyID; // PolyID[i] is the TIGER ID for // the i-th polygon Point2 * PolyIntPt; // PolyIntPt[i] is an interior point // of the i-th Polygon /////////////////////////////////////////////////////// // Edge List /////////////////////////////////////////////////////// int * LineID; // LineID[i] is the TLine ID // of the i-th Tline }//class TigerSubdiv
Write the following two member functions:
void TigerSubdiv::loadCounty(char * dirName);
void TigerSubdiv::display(void);
The first subroutine reads from a set of TIGER files into the TigerSubdiv structure. It assumes that the directory ``dirName'' contains all the TIGER files of a single county. The second subroutine displays the current TigerSubdiv. Your programs in hw1 should be modified and re-used here: all the functionalities of hw1 should be here.
Please put the solution to Stage A in the file called hw2A.cc. Please compile the programs from hw1 (and possibly others) in separate files called hw2A1.cc, hw2A2.cc, etc). But hw2A.cc should have its own main program which tests the routine. I should be able to type "make a" to automatically compile, load the the tiger data from a county, and display the subdivision.
The simplest simplification is to extract the following data from a map: display an outline of the land and water bodies. For TIGER data, we also have two special kind of land areas: park and landmarks. Thus each polygon in the subdivision has one of the following types:
TigerSubdiv extractOutline(TigerSubdiv & ts);
This subroutine returns a TigerSubdiv that represents the outline. Put this program in a file called hw2B.cc Again, "make b" should automatically compile and run a simple test to extract and display an outline.
The next stage is to extract the transportation network. For simplicity, we restrict attentions only roads, which include highways (called limited access roads) and local roads. In particular, we ignore railroads. Doing this is very similar to the way we extract outlines. The only difference is that we now remove TLines do not represent roads. AS USUAL, we ensure that the TLines we remove really do join two regions; otherwise we tag it as ``isthmus''. Write an subroutine
TigerSubdiv extractRoads(TigerSubdiv & ts);
This should be in hw2C.cc and tested with "make c".
We call this ``simple simplification" because we only perform the ``edge collapse'' operation (two vertices are replaced by one) but not its inverse ``vertex split''. In general, the term ``edge collapse'' is a misnomer because the two vertices NEED not form an edge (or TLine).
When simplifying, we first try to remove detail points. Each simplification step is controlled by a parameter ScreenScale (or SScale). A SScale of 100 intuitively means ``100 meters per pixel''. We need a simple algorithm for converting lat/long distances into ScreenScale (see Hint Below). We first implement the Douglas-Peucker algorithm for simplifying the detail points of a TLine. A single step of the Douglas-Peucker algorithm takes a TLine and a SScale parameter as argument, and it computes the Tline with ``as few'' detail points as possible such that the error does not exceed the SScale. Write the following subroutines to perform this simplification:
// Copy constructor
TigerSubdiv(TigerSubdiv &);
// DPeuckerStep is subroutine for DPeucker
void TigerSubdiv::DPeuckerStep(int lineID, int SScale);
// Main routine that repeatedly calls DPeuckerStep
void TigerSubdiv::DPeucker(int SScale);
Note that DPeucker modifies the TigerSubdiv structure, so you may want to use the copy constructor first. Put the program in the file hw2D.cc. I want some statistics to be printed when I test the program using "make d". For instance, it should show me how many points are removed as a result of calling DPuecker with various SScales.
REMARK: already, our polygons are ``not regular'' because of the presense of canals/isthmuses. We admit further irregularities when we do simplification: there may be ``pinch points'', the polygon may contain ``causeways'' (whose removal breaks the polygon into two disjoint parts).
THIS PART IS OPTIONAL FOR THE PRESENT HOMEWORK. We continue with simple simplification. We now wish to allow the removal of Lines and polygons in our simplification. We use the basic scheme of Hoppe and others: create a priority queue of all candidate pairs (P,Q) of endpoints that might be merged (or collapsed). We will assume that the result of the merging is re-assigned to either P or Q (in this case, it is possible to prove that the resulting graph remains PLANAR in the sense there exists a suitable insertion of detail points such that the graph embedding is proper. What candidate pairs to consider? We do not want to consider ALL quadratic number of pairs. Certainly if P and Q are endpoints of some TLine, then (P,Q) is a candidate. The rule is this: (P,Q) is a candidate iff Q is a boundary point of a region in star(P) (recall this definition from lecture). We also need a ``distance function'' d(P,Q). This function can be the same function used for determining the "SScale" distance in Stage D (see Hints for Stage D). Now, we put each candidate (P,Q) into the priority queue, with priority given by d(P,Q). We now have a while-loop: as long as the queue is non-empty, and the minimum distance in the queue is less than our SScale parameter, we remove a pair (P,Q) from the queue and perform a merge. After we make the merge, we have to update the distance value for some of the candidate pairs.
Please review the instructions found in Homework 1. In particular, there should be a Makefile that contains the targets ä", "b", etc.
IN ALL NUMERICAL COMPUTATIONS, WE ASK YOU NOT TO WORRY ABOUT ROUNDOFF ERRORS. WE WILL INDICATE HOW TO DEAL WITH THEM LATER. ALSO, DO NOT WORRY ABOUT DEGENERACIES.
Above, we have a convention for the direction of the half-edges that bounds a polygon. To enforce this convention, you will need to check if a point is to the left or right of a directed point, write a simple predicate called int LeftTurn(p,q,r) which returns +1 if point r is to the left of the directed line (p,q); returns -1 if to the right; returns 0 if the 3 points are collinear. This is just the sign of the determinant det[ P | Q | R] where P, Q, R are 3-dimensional vectors obtained from p, q, r by adding a ``1'' to the 3-rd coordinate. If there are NO interior points, it is easy to determine if a half-edge or its twin bounds a polygon: if p, q are the endpoints of a TLine, and r is an interior point of a polygon G, then the half-edge (p,q) bounds G iff LeftTurn(p,q,r) = -1. BUT because of "detail points", it is actually a little tricky to get this test correct. FOR NOW, simply use the above test as if there are NO detail points.