[0001] The present invention relates to a computer-implemented method and apparatus for automatically labeling maps or graph layouts in accordance with predefined label criteria.
[0002] Maps include geographic drawings showing countries, cities, rivers, bodies of water, mountains, and other features of interest. Labeling cartographic features is a fundamental part of map-making. Placing each label optimally with respect to its corresponding feature invariably produces labels overlapping each other or too close to each other. As this results in confusion and unacceptable maps, methods to reposition labels or not draw them at all must be used to create a map that conveys as much information as possible.
[0003] Tagging graphical objects with text labels is a fundamental task in the design of many types of informational graphics. This problem is seen in its most essential form in cartography, but it also arises frequently in the production of other informational graphics such as scatter plots. The quality of a labeling is determined essentially by the degree to which labels obscure other labels or features of the underlying graphic. The goal is to choose positions for the labels that do not give rise to label overlaps and that minimize obscuration of features. Construction of good labeling is thus a combinatorial optimization problem, which has been shown to be NP-hard (Marks and Shieber, 1991). As a hypothetical baseline algorithm, randomly choosing positions for each label generates poor labeling, both aesthetically, and as quantified using a metric that counts the number of conflicted labels, i.e., those that obscure point features or other labels.
[0004] In addition to geographical and technical maps, there are many labeling applications relating to graph layouts and drawings. These applications include, but are not limited to, areas such as database design (e.g. entity relationship diagrams), software engineering including CASE, software debugging, complex web pages, CAD drafting, complex electrical diagrams, and telecommunications and communications networking. In fact, the labeling of the graphical features of any drawing is generally necessary because it conveys information essential to understanding the drawing. For complex and information rich drawings, computer aided labeling is increasingly employed.
[0005] As used in the present specification, the term “map” is used to include both geographical and technical maps as well as graph layouts and drawings. The term “label” is used to refer to text or other indicia to be placed on a map.
[0006] A system and method for labeling objects on maps while avoiding collisions with other labels has been sought after. Some apparently powerful algorithms for automatic label placement on maps use heuristics that capture considerable cartographic expertise but are hampered by provably inefficient methods of search and optimization.
[0007] This patent discloses a system and method for label placement that achieves the twin goals of practical efficiency and high labeling quality by employing cartographic heuristics.
[0008] The present invention provides a computer-implemented system and method of automatically labeling a map in accordance with predefined label location, placement, and priority criteria.
[0009] Here, each label is represented as a convex polygon with any orientation on the map. Labels have various parameters associated with them such as location, size, shape, number and location of vertices, target feature, priority, movement constraints, and clearance. After finding the best position of a label for every feature without regard to other labels or features, higher priority label positions are compared to lower priority label positions two at a time. If the labels interfere, the lower priority label is moved within its movement constraint. Several candidate locations for the lower priority label position are found by moving it the shortest distance to avoid the higher priority label position. A new location is acceptable if the location does not collide with a label of higher priority. It can collide with a label of lower priority. If no candidate positions are acceptable, the label is not moved. This process continues until all labels are inspected, after which a deviation from the desired result function is calculated. This function is zero if the label interference for all labels is zero and greater than zero otherwise. The whole process is repeated until the evaluation function equals zero or the change in the evaluation function is less than a given percent (e.g., two percent) for a small number (e.g., four) of iterations or if it oscillates for a number (e.g., six) of iterations or if the number of iterations is greater than a set number (e.g., twenty). If any interference remains, then interfering labels with lower priorities are not drawn.
[0010] The details of the present invention, both as to its structure and operation, can best be understood in reference to the accompanying drawings, in which like reference to the accompanying drawings, in which like reference numerals refer to like parts, and in which:
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024] Referring initially to
[0025] One or more features of the computer as shown may be omitted while still permitting the practice of the invention. For example, printer
[0026] The flow charts herein illustrate the structure of the logic of the present invention as embodied in computer program software. Those skilled in the art will appreciate that the flow charts illustrate the structures of logic elements, such as computer program code elements or electronic logic circuits, that function according to this invention. Manifestly, the invention is practiced in its essential embodiment by a machine component that renders the logic elements in a form that instructs a digital processing apparatus (that is, a computer) to perform a sequence of function steps corresponding to those shown.
[0027]
[0028] Modules a, b, c, d may be a computer program that is executed by processor
[0029]
[0030] To begin, the user must specify how to initially place labels on a map. That is, commencing at routine
[0031] Next, at routine
[0032] The following discussion concerns only those geometric objects in the plane of the map, of which the labels are a part. All labels are restricted to convex planar polygons in this plane. A planar polygon is convex if it contains all the line segments connecting any pair of its points. If two convex planar polygons overlap, this means that:
[0033] 1) at least one vertex of one polygon is inside the other polygon, or
[0034] 2) at least one edge of one polygon crosses or touches (i.e., intersects) an edge of the other polygon.
[0035] To begin the anti-collision procedure, three initialization steps occur. First, labels lying partially inside the map boundary must either be moved completely inside the portrait or be excluded from being compared to other labels and excluded from being drawn. Each label has movement types and constraints that determine whether or not the label qualifies for movement completely onto the map. These movement types and constraints are explained below. Labels qualifying for movement to the inside of the map are moved regardless of the collision status with any other label.
[0036] Second, the labels must be ordered in a list with respect to priority from highest priority to lowest priority. In general, many labels will have the same priority. Within any group of labels with the same priority, any particular label is randomly placed within that block.
[0037] Third and last, variables that monitor the state of the procedure must be initialized.
[0038] The purpose of routine
[0039] At routine
[0040] Every combination of two labels is tested for overlap in routine
[0041] The overlap test at routine
[0042] Labels are moved about the map at routine
[0043] The evaluation function, routine
[0044] After the iterations stop, all labels are examined for any overlap and label properties are adjusted at routine
[0045]
[0046] Step
[0047] Each cell is tested, step
[0048] The fraction of the label inside the map boundary is determined at step
[0049] The above-described logic is further shown in the following pseudo-code with comments:
PULL IN THE LABELS FROM THE EDGES OF THE MAP ROUTINE //Pseudo-code for the Initialization of the Anti-collision Procedure for Maps //List of pseudo-code variables previous_collision_score - the collision score from the previous iteration previous_previous_collision_score - the collision score from two iterations ago iteration_count - number of times the anti-collision procedure has looped slow_change_count - number of iterations of continuous slow change of collision score oscillation_count - number of iterations of continuous oscillation of collision score priority_of_most_important_label - numerical priority value of the most important label priority_range - the difference between the priority of the least and the most important labels. This number is non-negative. frac_inside - fraction of label inside the map boundaries map_x_size - the number of x units in the map - map boundary is a rectangle map_y_size - the number of y units in the map - map boundary is a rectangle (xMove2D, yMove2D) - the label movement if the label qualifies for movement completely inside the map boundary and the label parameters specify 2D type movement (xMoveVec[], yMoveVec[]) - an array of label movements if the label qualifies for movement completely inside the map boundary and the label parameters specify vector type movement (xc, yc) - center point of a cell formed from a grid within the circumscribing rectangle around the label (x_IP, y_IP) - a point satisfying various conditions used to properly move a label completely inside the map boundary LABEL_TOO_MUCH_OUTSIDE_PORTRAIT - indicates if the procedure has determined that the label has much area outside the map boundary or can not be properly moved to a new position completely inside the map boundary. This is a flag of every label set by the procedure. LABEL_OUTSIDE_PORTRAIT - Not used. This is a flag of every label set by the procedure. LABEL_MOVED_INTO_PORTRAIT - indicates if the procedure has moved a label that was originally partially outside the map boundary to a new position completely inside the map boundary. This is a flag of every label set by the procedure. LABEL_MIN_FRACTION_INSIDE - minimum fraction of the label that must be inside the map boundary to attempt relocation completely inside the map boundary. This is a parameter of every label set by caller. LABEL_NEW_LOCATION - A vector (x, y) which is added to all vertices of a label if the procedure moves the label. This vector has an initial value of (0, 0) . This is a parameter of every label determined by the procedure. // pseudo-code also has: // a list of possible label movement candidates to pull the label inside the map boundary // a data structure map of labels and their properties sorted by priority from the most // important label to the least important label --------------------------------------------------------
---- // THIS IS THE START OF ROUTINE 10 // The labels are not in any particular order at this point. // They are only in the order in which they are received from the caller. for i = first unordered label to last unordered label set label i flag LABEL_OUTSIDE_PORTRAIT = FALSE set label i flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = FALSE set label i flag LABEL_MOVED_INTO_PORTRAIT = FALSE set label i parameter LABEL_NEW_LOCATION = (0, 0) // Determine if the label is inside or outside the map boundary. // If all vertices are inside, then the entire label is inside. // Here, a vertex on the map boundary is inside the boundary. label_inside_map = TRUE for j = first vertex of label i to last vertex of label i if ( vertex j outside map boundary ) { label_inside_map = FALSE } next j if ( label_inside_map = FALSE ) { // Below, find the approximate fraction of the label inside the map boundary. // The circumscribing rectangle has edges parallel to the map edges. // Note that both the rectangle and the label are convex polygons. Put a circumscribing rectangle around label i Divide the circumscribing rectangle into 64 units by 64 units forming 4096 cells in_label = 0 in_label_and_map = 0 for k = first cell to last cell Find center point of cell k called (xc, yc) // Here, a point on a label edge or map boundary is inside the label or map. // Use the “point inside convex polygon” procedure described in the // labels overlap section. if( (xc, yc) inside label ) { in_label ≡ in_label + 1 if( (xc, yc) inside map boundary ) { in_label_and_map = in_label_and_map + 1 } } next k frac_inside = ( in_label_and_map )/( in_label ) // Move the label inside the map boundary if enough of the label is inside. // Some of the vertices below may be the same vertex. (x_low, yL) = coordinates of vertex with lowest x coordinate (x_high, yH) = coordinates of vertex with highest x coordinate (xL, y_low) = coordinates of vertex with lowest y coordinate (xH, y_high) = coordinates of vertex with highest y coordinate // find the new location for the label if ( frac_inside > LABEL_MIN_FRACTION_INSIDE parameter of label i ) { if ( 2D type movement for label i ) { (xMove2D, yMove2D) = (0, 0) // If both conditions are true, the label will not fit into the map. if ( x_low < 0 ) { xMove2D = 0 − x_low } else if ( x_high > map_x_size − 1 ) { xMove2D = map_x_size − 1 − x_high } // If both conditions are true, the label will not fit into the map. if ( y_low < 0 ) { yMove2D = 0 − y_low } else if ( y_high > map_y_size − 1 ) { yMove2D = map_y_size − 1 − y_high } // Determine if the label is still within its movement parameters. // This means has the label moved too far from its original position. // The original location parameter is never changed. It does not change // because it is always used for comparison to the new position. if ( (xMove2D, yMove2D) within label i 2D type movement parameters ) { // Determine if the label is still inside the map boundary after movement. // This is really a test to see if the label is too big to fit in the map. // Here, a vertex on the map boundary is not outside the map. // This test works because both label and map are convex polygons. for j = first vertex of label i to last vertex of label i label_moved_outside_map = FALSE if ( ( vertex j + (xMove2D, yMove2D) ) of label i is outside map boundary ) { label_moved_outside_map = TRUE } next j if ( label_moved_outside_map = FALSE ) { set label i flag LABEL_MOVED_INTO_PORTRAIT = TRUE set label i parameter LABEL_NEW_LOCATION = (xMove2D, yMove2D) } } } else { // vector type movement count = 0 // If both conditions are true, the label will not fit into the map. if ( x_low < 0 ) { find a point (x_IP, y_IP) which meets the following requirements contained by a line parallel to the vector type movement contained by a the line x = 0 contained by a line also containing (x_low, yL) if ( (x_IP, y_IP) exists ) { xMoveVec[count] = x_IP − x_low yMoveVec[count] = y_IP − yL place in list of possible label movement candidates count = count + 1 } } else if ( x_high > map_x_size − 1 ) { find a point (x_IP, y_IP) which meets the following requirements contained by a line parallel to the vector type movement contained by a the line x = map_x_size − 1 contained by a line also containing (x_high, yH) if ( (x_IP, y_IP) exists ) { xMoveVec[count] = x_IP − x_high yMoveVec[count] = y_IP − yH place in list of possible label movement candidates count = count + 1 } } // If both conditions are true, the label will not fit into the map. if ( y_low < 0 ) { find a point (x_IP, y_IP) which meets the following requirements contained by a line parallel to the vector type movement contained by a the line y = 0 contained by a line also containing (xL, y_low) if ( (x_IP, y_IP) exists ) { xMoveVec[count] = x_IP − xL yMoveVec[count] = y_IP − y_low place in list of possible label movement candidates count = count + 1 } } else if ( y_high > map_y_size − 1 ) { find a point (x_IP, y_IP) which meets the following requirements contained by a line parallel to the vector type movement contained by a the line y = map_y_size − 1 contained by a line also containing (xH, y_high) if ( (x_IP, y_IP) exists ) { xMoveVec[count] = x_IP − xH yMoveVec[count] = y_IP − y_high place in list of possible label movement candidates count = count + 1 } } // Can have zero, one, or two possible label movement candidates for k = 0 to (count − 1) // Determine if the label is still within its movement parameters. // This means has the label moved too far from its original position. // The original location parameter is never changed. It does not change // because it is always used for comparison to the new position. move_distance = magnitude of (xMoveVec[k], yMoveVec[k]) if ( move_distance within label i vector type movement parameters ) { // Determine if the label is still inside the map boundary after movement. // This is really a test to see if the label is too big to fit in the map. // Here, a vertex on the map boundary is not outside the map. // This test works because both label and map are convex polygons. for j = first vertex of label i to last vertex of label i label_moved_outside_map = FALSE if(( vertex j + (xMoveVec[k], yMoveVec[k])) of label i is outside map boundary) { label_moved_outside_map = TRUE } next j if ( label_moved_outside_map = FALSE ) { set label i flag LABEL_MOVED_INTO_PORTRAIT = TRUE set label i parameter LABEL_NEW_LOCATION = (xMoveVec[k], yMoveVec[k]) break out of loop // go past next k } } next k } // end of vector type movement } if ( label i flag LABEL_MOVED_INTO_PORTRAIT = FALSE ) { set label i flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE } } // end if label_inside_map = FALSE next i // THIS IS THE START OF ROUTINE 20 // Sort the labels by priority. // The labels with the highest priorities have the lowest numbers. // Priorities may be negative numbers. // Labels may have the same priority. // After this loop, assume all labels are ordered properly. Sort labels by priority from the most important label to the least important label and place into a data structure map // THIS IS THE START OF ROUTINE 30 // Initialize halting criteria variables priority_range = priority_of_least_important_label − priority_of_most_important_label // initialize these two variables to large numbers previous_collision_score = Very Large Number previous_previous_collision_score = Very Large Number iteration_count = 0 slow_change_count = 0 oscillation_count = 0
[0050] Referring to
[0051] The above-described logic is further shown in the following pseudo-code with comments:
Order of Comparison for the Label Overlap Test Routine // The n labels have already been sorted in priority order, // from the most important, label 0, consecutively, // to the least important, label (n − 1). LABEL_TOO_MUCH_OUTSIDE_PORTRAIT - indicates if the procedure has determined that the label has much area outside the map boundary or can not be properly moved to a new position completely inside the map boundary. This is a flag of every label set by the procedure. LABEL_OUTSIDE_PORTRAIT - Not used. This is a flag of every label set by the procedure. LABEL_MOVED_INTO_PORTRAIT - indicates if the procedure has moved a label that was originally partially outside the map boundary to a new position completely inside the map boundary. This is a flag of every label set by the procedure. last_Label_Index = number_of_labels − 1; // zero based // Zero based. // The highest priority is zero and the lowest priority is a number greater than zero. // Note that there may be priorities which have no labels. last_Pri = lowest priority − highest priority; // which equal the lowest priority // Below, if there are no labels with priority p, // first_Pri[p] = −1 and last_Pri[p] = −1 // first_label[p] = first label index with priority p // last_label[p] = last label index with priority p for p = 0 to last_Pri; // highest priority to lowest priority if labels with priority p exist first_label[p] = most important label with priority p; last_label[p] = least important label with priority p; else first_label[p] = −1; last_label[p] = −1; next p; for i_pri = 0 to last_Pri; // highest priority to lowest priority if first_label[i_pri] = −1, continue to next i_pri; for j_pri = i_pri to last_Pri; // highest priority to lowest priority if first_label[j_pri] = −1, continue to next j_pri; for i_idx = first_label[i_pri] to last_label[i_pri]; if i_idx flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE OR LABEL_OUTSIDE_PORTRAIT = TRUE, continue to next i_idx for j_idx = first_label[j_pri] to last_label[j_pri]; // Do not compare a label to itself or // compare labels which have been previously compared, // for this particular iteration of the entire algorithm. if i_idx <= j_idx, continue to next j_idx; if j_idx flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE OR LABEL_OUTSIDE_PORTRAIT = TRUE, continue to next j_idx if label i_idx overlaps label j_idx, then perform the label movement procedure on label j_idx; next j_idx; next i_idx; next j_pri; next i_pri;
[0052] All labels are restricted to convex planar polygons in the plane of the map. A planar polygon is convex if it contains all the line segments connecting any pair of its points. If two convex planar polygons overlap, this means that
[0053] 1) at least one vertex of one polygon is inside the other polygon, or
[0054] 2) at least one edge of one polygon intersects an edge of the other polygon Routine
[0055]
[0056] where
[0057] (x,y) is any point on the line, and
[0058] (X1,Y1) and (X2,Y2) are the endpoints of an edge of the polygon under test. Points lying on the polygon edges satisfy the line equations, while points not on the polygon edges do not satisfy those equations. If (x, y) is any point in the plane, the equation for a line containing an edge is:
[0059] where K is a real number constant.
[0060] Then, for all points to the left of any edge, K>0, and for all points to the right of any edge, K<0. Note that point
[0061] Therefore:
[0062] A point that is above a line pointing to the right is a point that lies to the left of the line. Similar arguments show that any point on the left of lines pointing up, pointing down, or pointing left yields a positive value with substituted into the line equation. Step
[0063] The above-described logic is further shown in the following pseudo-code with comments:
Pseudo-code for the Overlap Test of Convex Planar Polygons List of pseudo-code variables (x_2_i, y_2_i) - vertex i of polygon 2 (X1_j, Y1_j) - vertex 1 of edge j of polygon 1 (X2_j, Y2_j) - vertex 2 of edge j of polygon 1 (x_IP, y_IP) - intersection point of lines containing edges x_max_i - max x of edge i y_min_j - min y on edge j find max x, max y, min x, min y on polygon 1 - each will be on a vertex find max x, max y, min x, min y on polygon 2 - each will be on a vertex // if any expression is true, the polygons do not overlap, so return false if (min x of polygon 1 >= max x of polygon 2) RETURN NO_OVERLAP if (min x of polygon 2 >= max x of polygon 1) RETURN NO_OVERLAP if (min y of polygon 1 >= max y of polygon 2) RETURN NO_OVERLAP if (min y of polygon 2 >= max y of polygon 1) RETURN NO_OVERLAP // if any vertex of polygon 2 is inside polygon 1, the result is greater than zero. // proceed around polygon 1 in the CCW direction for each vertex of polygon 2 for i = first vertex of polygon 2 to last vertex of polygon 2 inside = TRUE for j = first edge of polygon 1 to last edge of polygon 1 in CCW direction if((y_2_i − Y1_j) (X2_j − X1_j) − (x_2_i − X1_j) (Y2_j − Y1_j) <= 0) inside = FALSE next j if (inside = TRUE), RETURN OVERLAP next i Repeat the above, except test polygon 1 vertices with polygon 2 edges Return OVERLAP if appropriate // perform the edge intersection test for i = first edge of polygon 1 to last edge of polygon 1 of the two endpoints of edge i, get x_max_i, y_max_i, x_min_i, y_min_i for j = first edge of polygon 2 to last edge of polygon 2 of the two endpoints of edge j, get x_max_j, y_max_j, x_min_j, y_min_j solve for intersection point, (x_IP, y_IP), of lines containing edge i and edge j if intersection point exists // An intersection at an endpoint is an overlap. // These tests also take care vertical and horizontal edges. if (x_IP <= x_max_i and x_IP >= x_min_i) and (y_IP <= y_max_i and y_IP >= y_min _i) and (x_IP <= x_max_j and x_IP >= x_min_j) and (y_IP <= y_max_j and y_IP >= y_min_j), RETURN OVERLAP next j next i RETURN NO_OVERLAP
[0064] Labels must be moved about the map to clear existing label collisions. After it is determined that two labels overlap, routine
[0065] 1) the second label moves a shorter distance than other qualifying locations;
[0066] 2) the second label movement does not result in overlap with another label (or labels) of equal or higher priority than the first label;
[0067] 3) the second label movement does not exceed the maximum movement parameters for that particular label; and
[0068] 4) no part of the second label is moved outside the map boundary.
[0069] If no candidate locations meet these criteria, the second label is not moved. During the process of fixing existing collisions, other collisions may be created. New collisions are only allowed if it reduces collisions among labels with priorities equal to or higher than the first label. As the procedure iterates, new collisions are handled like the original collisions. The procedure will minimize collisions.
[0070] Labels may be moved in one of two ways. First, a label may move in any of the four directions in the map's coordinate system, +X, −X, +Y, −Y, up to a maximum distance from the original location. This is referred to as 2D type movement. Second, a label may move along a vector up to a maximum distance from the original location in the positive vector direction or the negative vector direction. This is referred to as vector type movement. Both the vector and the maximum distances are in the label's parameter list.
[0071] Given two labels to compare, a first label's edges are traversed in a CCW direction. Step
[0072] If in either the 2D type movement case or the vector type movement case, an intersection exists and the vertex is on the inside side, step
[0073] Step
[0074] 1) Find the maximum length in the length list and a corresponding qualified vector from the vector list;
[0075] 2) Insert the length and the qualified vector into a data structure map that is sorted by distance; and
[0076] 3) Empty the length list and vector list.
[0077] After all the edges of the first label are checked, at step
[0078] Next, tests are performed to determine if proposed locations for the second label are acceptable. At step
[0079] The above-described logic is further shown in the following pseudo-code with comments:
Movement Procedure of Convex Planar Polygons // List of pseudo-code variables (x_2_j, y_2_j) - vertex j of polygon 2 (X1_i, Y1_i) - vertex 1 of edge i of polygon 1 (X2_i, Y2_i) - vertex 2 of edge i of polygon 1 (x_IP, y_IP) - intersection point of lines containing edge and vertex (X,Y) - vector from vertex to edge pseudo-code also has: a list of distances a list of vectors a data structure map of distances and vectors sorted by distance, short to long // Polygon 1 is the more important polygon and polygon 2 will move if possible // Here, the vertices in a polygon are on the left side of the edge // of the other polygon when traversing it in the CCW direction, // but the vertices are not necessarily inside the other polygon. // That is why all possibilities are caught in the algorithm below - // even where no vertex from either polygon is inside the other. // Do not have to check specifically for the above case. // If a vertex of polygon 2 is on left side a polygon 1 edge, the result is greater than zero. // proceed around polygon 1 in the CCW direction for each vertex of polygon 2 // Note the the vertex in question does not have to be inside polygon 1 for i = first edge of polygon 1 to last edge of polygon 1 in CCW direction count_of_possible_vertices = 0 for j = first vertex of polygon 2 to last vertex of polygon 2 if((y_2_j − Y1_i)(X2_i − X1_i) − (x_2_j − X1_i) (Y2_i − Y1_i) > 0) if (2D type movement for polygon 2) // a solution will always exist for this case solve for intersection point, (x_IP, y_IP), of a line containing edge i and a line perpendicular to edge i containing (x_2_j, y_2_j) if (vector type movement for polygon 2) // a solution might not exist for this case solve for intersection point, (x_IP, y_IP), of a line containing edge i and a line parallel to the vector type movement containing (x_2_j, y_2_j) if ( solution exits for (x_IP, y_IP) ) // get vector from vertex to intersection point (X,Y) = (x_IP − x_2_j, y_IP − y_2_j) if ( (X,Y) length minute ) if ( 2D type movement for polygon 2 ) find a point (X,Y) which meets the following requirements on right side of edge i (CCW) contained by a line perpendicular to edge i contained by a line also containing (x_IP, y_IP) a minute distance from (x_IP, y_IP) else // vector type movement for polygon 2 find a point (X,Y) which meets the following requirements on right side of edge i (CCW) contained by a line parallel to the vector type movement contained by a line also containing (x_IP, y_IP) a minute distance from (x_IP, y_IP) // because polygon may move several times, keep the original location of the label if ( movement of (X,Y) leaves polygon with movement limit ) // Make vector just a bit larger that the distance to the edge // so when polygon 2 is moved, it moves just outside the polygon 1 length_of_XY = length of (X, Y) * (1.0 + 1.0e−09) X = X * (1.0 + 10e−09) Y = Y * (1.0 + 10e−09) append length_of_XY to end of distance list append (X,Y) to end of vector list count_of_possible_vertices = count_of_possible_vertices + 1 next j if (count_of_possible_vertices > 0) find the maximum distance in the distance list get the corresponding vector to this distance from the vector list insert the distance and the vector into the data structure map sorted by distance, from the shortest distance to the longest distance empty distance list and vector list next i Repeat the above, except use polygon 1 vertices and with polygon 2 edges The vector for possible movement, (X,Y), is reversed Insert the results into the same distance/vector data structure map // the outer loop is just going thought the sorted data structure map for i = first location candidate to last location candidate get new location for polygon by adding vector (X,Y) to each vertex if ( any part of label outside map boundary ) next i for j = first label to last label whose priority >= polygon 1 if ( polygon 1 is label j or polygon 2 is label j) next j if ( polygon 2 in location candidate i overlaps label j ) next i update polygon 2 location in its parameter list break out of both for loops next j next i clear the data structure map get the next pair of labels to be tested for overlap
[0080] The Evaluation Function, the Halting Criteria, and the Adjustment of Label Properties
[0081] The calculation of the evaluation function is represented by
[0082] Collision Score=
[0083] where the score is the summation over every pair of overlapping labels. The result of this function is defined as zero if no collisions remain and greater than zero if any collisions remain. The function penalizes disproportionately for collisions involving high priority labels. For instance, a collision involving a high priority label and a low priority label gets a higher score (worse) than a collision involving two medium priority labels.
[0084] Routine
[0085] 1) the evaluation function is below a minimum value;
[0086] 2) the number of iterations is greater than a maximum value;
[0087] 3) the evaluation function changes less than a minimum percentage of the previous iteration for more than a set number of iterations; and
[0088] 4) the evaluation function oscillates for more than a set number of consecutive iterations.
[0089] If none of these conditions are meet, the anti-collision algorithm is repeated, noting that labels may move several times before the iterations stop. A label's new position is stored in its parameter list at the time a label is moved. The original position is always available in the label's parameter list.
[0090] Routine
[0091] 1) If one label has MUST DRAW=TRUE, that label sets DRAW=TRUE, and the second label sets DRAW=FALSE.
[0092] 2) If both labels have MUST DRAW=TRUE, the label higher on the list of label priority sets DRAW=TRUE, and the other label sets DRAW=FALSE. Note that this will hold for labels of equal priority.
[0093] 3) If neither label has MUST DRAW=TRUE, the label higher on the list of label priority sets DRAW=TRUE, and the other label sets DRAW=FALSE. Note that this will hold for labels of equal priority.
[0094] The label priority list and the overlap test are described in preceding sections of the description of the entire anti-collision procedure.
[0095] After label properties are adjusted, control is returned to the caller,
[0096] The above-described logic is further shown in the following pseudo-code with comments.
[0097] Pseudo-code for the Evaluation Function, the Halting Criteria, and the Adjustment of Label Properties
List of pseudo-code variables collision_score - the sum of the evaluation function after each iteration previous_collision_score - the collision score from the previous iteration previous_previous_collision_score - the collision score from two iterations ago iteration_count - number of times the anti-collision procedure has looped slow_change_count - number of iterations of continuous slow change of collision score oscillation_count - number of iterations of continuous oscillation of collision score priority_of_most_important_label - numerical priority value of the most important label priority_range - the difference between the priority of the least and the most important labels. This number is non-negative. adjusted_priority_1 - the label 1 priority modified to make it work in the evaluation function // Initialize halting criteria variables priority_range = priority_of_least_important_label − priority_of_most_important_label // initialize these two variables to large numbers previous_collision_score = Very Large Number previous_previous_collision_score = Very Large Number iteration_count = 0 slow_change_count = 0 oscillation_count = 0 //------ The above must be done at the top of the procedure --------// // Evaluation Function ----------------------------------------------- collision_score = 0 // these loops go thought label priority list for i = first label to last label if( label i flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE OR label i flag LABEL_OUTSIDE_PORTRAIT = TRUE ) next i for j = label i+1 to last label if( label j flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE OR label j flag LABEL_OUTSIDE_PORTRAIT = TRUE ) next j if( label i and label j overlap ) { // Adjust the label priorities to make the evaluation function work properly. // Note that the highest priority labels are assigned the lowest numbers and // priorities may be positive or negative. adjusted_priority_1 = 1 + priority_range − ( label_1_priority − priority_of_most_important_label ) adjusted_priority_2 = 1 + priority_range − ( label_2_priority − priority_of_most_important_label ) collision_score = collision_score + (adjusted_priority_1)*(adjusted_priority_1) + (adjusted_priority_2)*(adjusted_priority_2) } next j next i // Halting Algorithm --------------------------------------- // is there slow change ? if(collision_score <= previous_collision_score AND collision_score > 0.98*previous_collision_score) { slow_change_count = slow_change_count + 1 } else { slow_change_count = 0 } // is there oscillation ? if( (collision_score > previous_collision_score AND previous_collision_score < previous_previous_collision_score ) OR (collision_score < previous_collision_score AND previous_collision_score > previous_previous_collision_score ) ) { oscillation_count = oscillation_count + 1 } else { oscillation_count = 0 } iteration_count = iteration_count + 1 previous_previous_collision_score = previous_collision_score previous_collision_score = collision_score if(collision_score = 0) goto ADJUST_LABEL_PARAMETERS if(iteration_count > 20) goto ADJUST_LABEL_PARAMETERS if(slow_change_count > 4) goto ADJUST_LABEL_PARAMETERS if(oscillation_count > 6) goto ADJUST_LABEL_PARAMETERS goto Start of Next Iteration ADJUST_LABEL_PARAMETERS: //------------------------------------------- // set label flag DRAW = TRUE for all labels for i = first label to last label label_i_DRAW = TRUE next i // these loops go thought label priority list and set the draw flag for i = first label to last label if( label i flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE OR label i flag LABEL_OUTSIDE_PORTRAIT = TRUE ) { label_i_DRAW = FALSE next i } for j = label i+1 to last label if( label j flag LABEL_TOO_MUCH_OUTSIDE_PORTRAIT = TRUE OR label j flag LABEL_OUTSIDE_PORTRAIT = TRUE ) { label_j_DRAW = FALSE next j } if( label i and label j overlap ) { if ( label_i_MUST_DRAW = TRUE AND label_j_MUST_DRAW = TRUE ) { label_j_DRAW = FALSE } else if ( label_i_MUST_DRAW = TRUE ) { label_j_DRAW = FALSE } else if ( label_j_MUST_DRAW = TRUE ) { label_i_DRAW = FALSE } else { label_j_DRAW = FALSE } } next j next i return to caller
[0098] While the particular SYSTEM AND METHOD FOR LABELING MAPS as herein shown and described in detail is fully capable of attaining the above-described objects of the invention, it is to be understood that it is the presently preferred embodiment of the present invention and is thus representative of the subject matter which is broadly contemplated by the present invention, that the scope of the present invention fully encompasses other embodiments which may become obvious to those skilled in the art, and that the scope of the present invention is accordingly to be limited by nothing other than the appended claims, in which reference to an element in the singular means “at least one”. All structural and functional equivalents to the elements of the above-described preferred embodiment that are known or later come to be known to those of ordinary skill in the art are expressly incorporated herein by reference and are intended to be encompassed by the present claims. Moreover, it is not necessary for a device or method to address each and every problem sought to be solved by the present invention, for it to be encompassed by the present claims. Furthermore, no element, component, or method step in the present disclosure is intended to be dedicated to the public regardless of whether the element, component, or method step is explicitly recited in the claims.