polygons.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. function cell2Polygons(cell, x, y, settings) {
  2. var polygons = [];
  3. cell.polygons.forEach(function(p) {
  4. p.forEach(function(pp) {
  5. pp[0] += x;
  6. pp[1] += y;
  7. });
  8. if (settings.linearRing)
  9. p.push(p[0]);
  10. polygons.push(p);
  11. });
  12. return polygons;
  13. }
  14. function entry_coordinate(x, y, mode, path) {
  15. if (mode === 0) { /* down */
  16. x += 1;
  17. y += path[0][1];
  18. } else if (mode === 1) { /* left */
  19. x += path[0][0];
  20. } else if (mode === 2) { /* up */
  21. y += path[0][1];
  22. } else if (mode === 3) { /* right */
  23. x += path[0][0];
  24. y += 1;
  25. }
  26. return [ x, y ];
  27. }
  28. function skip_coordinate(x, y, mode) {
  29. if (mode === 0) { /* down */
  30. x++;
  31. } else if (mode === 1) { /* left */
  32. /* do nothing */
  33. } else if (mode === 2) { /* up */
  34. y++;
  35. } else if (mode === 3) { /* right */
  36. x++;
  37. y++;
  38. }
  39. return [ x, y ];
  40. }
  41. function requireFrame(data, lowerBound, upperBound) {
  42. var frameRequired,
  43. cols,
  44. rows,
  45. i,
  46. j;
  47. frameRequired = true;
  48. cols = data[0].length;
  49. rows = data.length;
  50. for (j = 0; j < rows; j++) {
  51. if ((data[j][0] < lowerBound) ||
  52. (data[j][0] > upperBound) ||
  53. (data[j][cols - 1] < lowerBound) ||
  54. (data[j][cols - 1] > upperBound)) {
  55. frameRequired = false;
  56. break;
  57. }
  58. }
  59. if ((frameRequired) &&
  60. ((data[rows - 1][0] < lowerBound) ||
  61. (data[rows - 1][0] > upperBound) ||
  62. (data[rows - 1][cols - 1] < lowerBound) ||
  63. (data[rows - 1][cols - 1] > upperBound))) {
  64. frameRequired = false;
  65. }
  66. if (frameRequired)
  67. for (i = 0; i < cols - 1; i++) {
  68. if ((data[0][i] < lowerBound) ||
  69. (data[0][i] > upperBound) ||
  70. (data[rows - 1][i] < lowerBound) ||
  71. (data[rows - 1][i] > upperBound)) {
  72. frameRequired = false;
  73. break;
  74. }
  75. }
  76. return frameRequired;
  77. }
  78. function requireLineFrame(data, threshold) {
  79. var frameRequired,
  80. cols,
  81. rows,
  82. i,
  83. j;
  84. frameRequired = true;
  85. cols = data[0].length;
  86. rows = data.length;
  87. for (j = 0; j < rows; j++) {
  88. if ((data[j][0] >= threshold) ||
  89. (data[j][cols - 1] >= threshold)) {
  90. frameRequired = false;
  91. break;
  92. }
  93. }
  94. if ((frameRequired) &&
  95. ((data[rows - 1][0] >= threshold) ||
  96. (data[rows - 1][cols - 1] >= threshold))) {
  97. frameRequired = false;
  98. }
  99. if (frameRequired)
  100. for (i = 0; i < cols - 1; i++) {
  101. if ((data[0][i] >= threshold) ||
  102. (data[rows - 1][i] > threshold)) {
  103. frameRequired = false;
  104. break;
  105. }
  106. }
  107. return frameRequired;
  108. }
  109. function traceBandPaths(data, cellGrid, settings) {
  110. var nextedge,
  111. path,
  112. e,
  113. ee,
  114. s,
  115. ve,
  116. enter,
  117. x,
  118. y,
  119. finalized,
  120. origin,
  121. cc,
  122. dir,
  123. count,
  124. point,
  125. found_entry;
  126. var polygons = [];
  127. var rows = data.length - 1;
  128. var cols = data[0].length - 1;
  129. /*
  130. * directions for out-of-grid moves are:
  131. * 0 ... "down",
  132. * 1 ... "left",
  133. * 2 ... "up",
  134. * 3 ... "right"
  135. */
  136. var valid_entries = [ ['rt', 'rb'], /* down */
  137. ['br', 'bl'], /* left */
  138. ['lb', 'lt'], /* up */
  139. ['tl', 'tr'] /* right */
  140. ];
  141. var add_x = [ 0, -1, 0, 1 ];
  142. var add_y = [ -1, 0, 1, 0 ];
  143. var available_starts = [ 'bl', 'lb', 'lt', 'tl', 'tr', 'rt', 'rb', 'br' ];
  144. var entry_dir = {
  145. bl: 1, br: 1,
  146. lb: 2, lt: 2,
  147. tl: 3, tr: 3,
  148. rt: 0, rb: 0
  149. };
  150. if (requireFrame(data, settings.minV, settings.maxV)) {
  151. if (settings.linearRing)
  152. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0], [0, 0] ]);
  153. else
  154. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0] ]);
  155. }
  156. /* finally, start tracing back first polygon(s) */
  157. cellGrid.forEach(function(a, i) {
  158. a.forEach(function(cell, j) {
  159. nextedge = null;
  160. /* trace paths for all available edges that go through this cell */
  161. for (e = 0; e < 8; e++) {
  162. nextedge = available_starts[e];
  163. if (typeof cell.edges[nextedge] !== 'object')
  164. continue;
  165. /* start a new, full path */
  166. path = [];
  167. ee = cell.edges[nextedge];
  168. enter = nextedge;
  169. x = i;
  170. y = j;
  171. finalized = false;
  172. origin = [ i + ee.path[0][0], j + ee.path[0][1] ];
  173. /* add start coordinate */
  174. path.push(origin);
  175. /* start traceback */
  176. while (!finalized) {
  177. cc = cellGrid[x][y];
  178. if (typeof cc.edges[enter] !== 'object')
  179. break;
  180. ee = cc.edges[enter];
  181. /* remove edge from cell */
  182. delete cc.edges[enter];
  183. /* add last point of edge to path arra, since we extend a polygon */
  184. point = ee.path[1];
  185. point[0] += x;
  186. point[1] += y;
  187. path.push(point);
  188. enter = ee.move.enter;
  189. x = x + ee.move.x;
  190. y = y + ee.move.y;
  191. /* handle out-of-grid moves */
  192. if ((typeof cellGrid[x] === 'undefined') ||
  193. (typeof cellGrid[x][y] === 'undefined')) {
  194. dir = 0;
  195. count = 0;
  196. if (x === cols) {
  197. x--;
  198. dir = 0; /* move downwards */
  199. } else if (x < 0) {
  200. x++;
  201. dir = 2; /* move upwards */
  202. } else if (y === rows) {
  203. y--;
  204. dir = 3; /* move right */
  205. } else if (y < 0) {
  206. y++;
  207. dir = 1; /* move left */
  208. } else {
  209. throw new Error('Left the grid somewhere in the interior!');
  210. }
  211. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  212. finalized = true;
  213. enter = nextedge;
  214. break;
  215. }
  216. while (1) {
  217. found_entry = false;
  218. if (count > 4)
  219. throw new Error('Direction change counter overflow! This should never happen!');
  220. if (!((typeof cellGrid[x] === 'undefined') ||
  221. (typeof cellGrid[x][y] === 'undefined'))) {
  222. cc = cellGrid[x][y];
  223. /* check for re-entry */
  224. for (s = 0; s < valid_entries[dir].length; s++) {
  225. ve = valid_entries[dir][s];
  226. if (typeof cc.edges[ve] === 'object') {
  227. /* found re-entry */
  228. ee = cc.edges[ve];
  229. path.push(entry_coordinate(x, y, dir, ee.path));
  230. enter = ve;
  231. found_entry = true;
  232. break;
  233. }
  234. }
  235. }
  236. if (found_entry) {
  237. break;
  238. } else {
  239. path.push(skip_coordinate(x, y, dir));
  240. x += add_x[dir];
  241. y += add_y[dir];
  242. /* change direction if we'e moved out of grid again */
  243. if ((typeof cellGrid[x] === 'undefined') ||
  244. (typeof cellGrid[x][y] === 'undefined')) {
  245. if (((dir === 0) && (y < 0)) ||
  246. ((dir === 1) && (x < 0)) ||
  247. ((dir === 2) && (y === rows)) ||
  248. ((dir === 3) && (x === cols))) {
  249. x -= add_x[dir];
  250. y -= add_y[dir];
  251. dir = (dir + 1) % 4;
  252. count++;
  253. }
  254. }
  255. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  256. /* we are back where we started off, so finalize the polygon */
  257. finalized = true;
  258. enter = nextedge;
  259. break;
  260. }
  261. }
  262. }
  263. }
  264. }
  265. if ((settings.linearRing) &&
  266. ((path[path.length - 1][0] !== origin[0]) ||
  267. (path[path.length - 1][1] !== origin[1])))
  268. path.push(origin);
  269. polygons.push(path);
  270. } /* end forall entry sites */
  271. }); /* end foreach i */
  272. }); /* end foreach j */
  273. return polygons;
  274. }
  275. function traceLinePaths(data, cellGrid, settings) {
  276. var nextedge,
  277. e,
  278. ee,
  279. cc,
  280. path,
  281. enter,
  282. x,
  283. y,
  284. finalized,
  285. origin,
  286. point,
  287. dir,
  288. count,
  289. found_entry,
  290. ve;
  291. var polygons = [];
  292. var rows = data.length - 1;
  293. var cols = data[0].length - 1;
  294. /*
  295. * directions for out-of-grid moves are:
  296. * 0 ... "down",
  297. * 1 ... "left",
  298. * 2 ... "up",
  299. * 3 ... "right"
  300. */
  301. var valid_entries = [ 'right', /* down */
  302. 'bottom', /* left */
  303. 'left', /* up */
  304. 'top' /* right */
  305. ];
  306. var add_x = [ 0, -1, 0, 1 ];
  307. var add_y = [ -1, 0, 1, 0 ];
  308. var entry_dir = {
  309. bottom: 1,
  310. left: 2,
  311. top: 3,
  312. right: 0
  313. };
  314. /* first, detect whether we need any outer frame */
  315. if (!settings.noFrame)
  316. if (requireLineFrame(data, settings.threshold)) {
  317. if (settings.linearRing)
  318. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0], [0, 0] ]);
  319. else
  320. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0] ]);
  321. }
  322. /* finally, start tracing back first polygon(s) */
  323. cellGrid.forEach(function(a, i) {
  324. a.forEach(function(cell, j) {
  325. nextedge = null;
  326. /* trace paths for all available edges that go through this cell */
  327. for (e = 0; e < 4; e++) {
  328. nextedge = valid_entries[e];
  329. if (typeof cell.edges[nextedge] !== 'object')
  330. continue;
  331. /* start a new, full path */
  332. path = [];
  333. ee = cell.edges[nextedge];
  334. enter = nextedge;
  335. x = i;
  336. y = j;
  337. finalized = false;
  338. origin = [ i + ee.path[0][0], j + ee.path[0][1] ];
  339. /* add start coordinate */
  340. path.push(origin);
  341. /* start traceback */
  342. while (!finalized) {
  343. cc = cellGrid[x][y];
  344. if (typeof cc.edges[enter] !== 'object')
  345. break;
  346. ee = cc.edges[enter];
  347. /* remove edge from cell */
  348. delete cc.edges[enter];
  349. /* add last point of edge to path arra, since we extend a polygon */
  350. point = ee.path[1];
  351. point[0] += x;
  352. point[1] += y;
  353. path.push(point);
  354. enter = ee.move.enter;
  355. x = x + ee.move.x;
  356. y = y + ee.move.y;
  357. /* handle out-of-grid moves */
  358. if ((typeof cellGrid[x] === 'undefined') ||
  359. (typeof cellGrid[x][y] === 'undefined')) {
  360. if (!settings.linearRing)
  361. break;
  362. dir = 0;
  363. count = 0;
  364. if (x === cols) {
  365. x--;
  366. dir = 0; /* move downwards */
  367. } else if (x < 0) {
  368. x++;
  369. dir = 2; /* move upwards */
  370. } else if (y === rows) {
  371. y--;
  372. dir = 3; /* move right */
  373. } else if (y < 0) {
  374. y++;
  375. dir = 1; /* move left */
  376. }
  377. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  378. finalized = true;
  379. enter = nextedge;
  380. break;
  381. }
  382. while (1) {
  383. found_entry = false;
  384. if (count > 4)
  385. throw new Error('Direction change counter overflow! This should never happen!');
  386. if (!((typeof cellGrid[x] === 'undefined') ||
  387. (typeof cellGrid[x][y] === 'undefined'))) {
  388. cc = cellGrid[x][y];
  389. /* check for re-entry */
  390. ve = valid_entries[dir];
  391. if (typeof cc.edges[ve] === 'object') {
  392. /* found re-entry */
  393. ee = cc.edges[ve];
  394. path.push(entry_coordinate(x, y, dir, ee.path));
  395. enter = ve;
  396. found_entry = true;
  397. break;
  398. }
  399. }
  400. if (found_entry) {
  401. break;
  402. } else {
  403. path.push(skip_coordinate(x, y, dir));
  404. x += add_x[dir];
  405. y += add_y[dir];
  406. /* change direction if we'e moved out of grid again */
  407. if ((typeof cellGrid[x] === 'undefined') ||
  408. (typeof cellGrid[x][y] === 'undefined')) {
  409. if (((dir === 0) && (y < 0)) ||
  410. ((dir === 1) && (x < 0)) ||
  411. ((dir === 2) && (y === rows)) ||
  412. ((dir === 3) && (x === cols))) {
  413. x -= add_x[dir];
  414. y -= add_y[dir];
  415. dir = (dir + 1) % 4;
  416. count++;
  417. }
  418. }
  419. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  420. /* we are back where we started off, so finalize the polygon */
  421. finalized = true;
  422. enter = nextedge;
  423. break;
  424. }
  425. }
  426. }
  427. }
  428. }
  429. if ((settings.linearRing) &&
  430. ((path[path.length - 1][0] !== origin[0]) ||
  431. (path[path.length - 1][1] !== origin[1])))
  432. path.push(origin);
  433. polygons.push(path);
  434. } /* end forall entry sites */
  435. }); /* end foreach i */
  436. }); /* end foreach j */
  437. return polygons;
  438. }
  439. export {cell2Polygons, traceBandPaths, traceLinePaths};