Go to Bottom of Page

Visualization, Fall 2001
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.

1  INTRODUCTION

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.

2  DETAILS

2.1  Stage A: Subdivision Data Structure based on Half-edges for TIGER data

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.

2.2  Stage B: Outline Extraction

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:

unused, water, land, park and landmarks.
Recall that these are the ``polygon types''. These are to be colored white, blue, yellow, green and pink. I will call this Outline Simplification. One issue is that the regions in a subdivision must be simply-connected polygonal regions. What if we have polygons with holes? The solution is simple - we allow ``isthmuses'' (or ``canals'' in case of water polygon) to join the outer boundary of a polygon to each of its islands. Here is how the algorithm proceeds: for each edge, we ask: do the polygons on each side of the edge have the same type? If so, we would like to remove that edge (and merge the 2 polygons). The ONLY reason you may not remove the edge is because the polygons on both side are identical! In this case, this edge is marked ``isthmus''. Implement this in the subroutine
	  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.

2.3  Stage C: Road Network Extraction

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".

2.4  Stage D: Simple Simplification (I)

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).

2.5  Stage E: Simple Simplification (II)

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.

3  SUBMITTING HOMEWORK

Please review the instructions found in Homework 1. In particular, there should be a Makefile that contains the targets ä", "b", etc.

4   HINTS

4.1  Stage A:

To load a county, you should initially read file types RT1, RT2 and RTP for the information about vertices, edges and polygons. Then read file type RTI which tells you the inter-connection between edges and polygons.

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.

4.2  Stage D:

I propose a simple way to convert screen scale into lat/long distances. Recall that each county stores its bounding box in lat/long. First compute the center C of this box. Then use some simple formulas to determine how long (in meters) is a distance of 1 unit (in long/lat coordinates). If p=(x,y) is a point where x=longitude, y=latitude, we can first convert this to a ``local coordinates'' p'=(x',y') where y'=y and x'= f(x), where f depends on the center C. The ``distance'' d(p,q) between two points p and q is now the Euclidean distance between p' and q'. Finally, we convert d(p,q) to meters (on the ground) by multiplying by a constant K that depends on C.


Go to TOP of Page




File translated from TEX by TTH, version 3.01.
On 20 Nov 2001, 11:25.