1 package net.systemeD.potlatch2.tools {
2         import net.systemeD.halcyon.Map;
3         import net.systemeD.halcyon.connection.CompositeUndoableAction;
4         import net.systemeD.halcyon.connection.Way;
5         import net.systemeD.halcyon.connection.Node;
6         import net.systemeD.halcyon.connection.MainUndoStack;
8         public class Simplify {
10                 private static const TOLERANCE:Number=0.00005;
12                 public static function simplify(way:Way, map:Map, keepOffscreen:Boolean):void {
13                         if (way.length<3) { return; }
15                         var action:CompositeUndoableAction = new CompositeUndoableAction("Simplify");
17                         var xa:Number, xb:Number;
18                         var ya:Number, yb:Number;
19                         var l:Number, d:Number, i:uint;
20                         var furthest:uint, furthdist:Number, float:Number;
21                         var n:Node;
23                         var tokeep:Object={};
24                         var stack:Array=[way.length-1];
25                         var anchor:uint=0;
26                         var todelete:Array=[];
28                         // Douglas-Peucker
29                         while (stack.length) {
30                                 float=stack[stack.length-1];
31                                 furthest=0; furthdist=0;
32                                 xa=way.getNode(anchor).lon ; xb=way.getNode(float).lon ;
33                                 ya=way.getNode(anchor).latp; yb=way.getNode(float).latp;
34                                 l=Math.sqrt((xb-xa)*(xb-xa)+(yb-ya)*(yb-ya));
36                                 // find furthest-out point
37                                 for (i=anchor+1; i<float; i+=1) {
38                                         d=getDistance(xa,ya,xb,yb,l,way.getNode(i).lon,way.getNode(i).latp);
39                                         if (d>furthdist && d>TOLERANCE) { furthest=i; furthdist=d; }
40                                 }
42                                 if (furthest==0) {
43                                         anchor=stack.pop();
44                                         tokeep[way.getNode(float).id]=true;
45                                 } else {
46                                         stack.push(furthest);
47                                 }
48                         }
50                         // Delete unwanted nodes, unless they're tagged or junction nodes
51                         for (i=1; i<way.length; i++) {
52                                 n=way.getNode(i)
53                                 if (tokeep[n.id] || n.hasTags() || n.parentWays.length>1 ||
54                                     (keepOffscreen && (n.lon<map.edge_l || n.lon>map.edge_r || n.lat<map.edge_b || n.lat>map.edge_t )) ) {
55                                         // keep it
56                                 } else {
57                                         // delete it
58                                         todelete.push(n);
59                                 }
60                         }
61                         for each (n in todelete) { n.remove(action.push); }
63                 }
65                 private static function getDistance(ax:Number,ay:Number,bx:Number,by:Number,l:Number,cx:Number,cy:Number):Number {
66                         // l=length of line
67                         // r=proportion along AB line (0-1) of nearest point
68                         var r:Number;
69                         if (l > 0) {
70                                 r=((cx-ax)*(bx-ax)+(cy-ay)*(by-ay))/(l*l);
71                         } else {
72                                 r=0;
73                         }
74                         // now find the length from cx,cy to ax+r*(bx-ax),ay+r*(by-ay)
75                         var px:Number=(ax+r*(bx-ax)-cx);
76                         var py:Number=(ay+r*(by-ay)-cy);
77                         return Math.sqrt(px*px+py*py);
78                 }
80         }
81 }