source: osm/applications/editors/josm/plugins/smed2/src/seamap/SeaMap.java@ 30164

Last change on this file since 30164 was 30164, checked in by malcolmh, 11 years ago

save

File size: 12.7 KB
Line 
1/* Copyright 2013 Malcolm Herring
2 *
3 * This is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License.
6 *
7 * For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
8 */
9
10package seamap;
11
12import java.util.*;
13
14import s57.S57att;
15import s57.S57att.Att;
16import s57.S57obj;
17import s57.S57obj.*;
18import s57.S57val;
19import s57.S57val.*;
20
21public class SeaMap {
22
23 public enum Nflag {
24 ANON, // Edge inner nodes
25 ISOL, // Node not part of Edge
26 CONN, // Edge first and last nodes
27 DPTH // Member of sounding array
28 }
29
30 public class Snode { // All coordinates in map
31 public double lat; // Latitude
32 public double lon; // Longitude
33 public Nflag flg; // Role of node
34 public double val; // Sounding value
35
36 public Snode() {
37 flg = Nflag.ANON;
38 lat = 0;
39 lon = 0;
40 val = 0;
41 }
42 public Snode(double ilat, double ilon) {
43 flg = Nflag.ANON;
44 lat = ilat;
45 lon = ilon;
46 val = 0;
47 }
48 public Snode(double ilat, double ilon, Nflag iflg) {
49 lat = ilat;
50 lon = ilon;
51 flg = iflg;
52 val = 0;
53 }
54 public Snode(double ilat, double ilon, Nflag iflg, double ival) {
55 lat = ilat;
56 lon = ilon;
57 flg = iflg;
58 val = ival;
59 }
60 }
61
62 public class Sarray { // A sounding array
63 public ArrayList<Long> nodes; // Sounding nodes
64
65 public Sarray() {
66 nodes = new ArrayList<Long>();
67 }
68 }
69
70 public class Edge { // A polyline segment
71 public long first; // First CONN node
72 public long last; // Last CONN node
73 public ArrayList<Long> nodes; // Inner ANON nodes
74
75 public Edge() {
76 first = 0;
77 last = 0;
78 nodes = new ArrayList<Long>();
79 }
80 }
81
82 public class Side { // An edge as used in a line or area feature
83 Edge edge; // Side is formed by this Edge...
84 boolean forward; // ... in this direction
85
86 public Side(Edge iedge, boolean ifwd) {
87 edge = iedge;
88 forward = ifwd;
89 }
90 }
91
92 public class Bound { // A single closed area
93 public boolean outer; // Role
94 ArrayList<Side> sides; // Sides that make up this area
95
96 public Bound() {
97 outer = true;
98 sides = new ArrayList<Side>();
99 }
100 public Bound(Side iside, boolean irole) {
101 outer = irole;
102 sides = new ArrayList<Side>();
103 sides.add(iside);
104 }
105 }
106
107 public class Area extends ArrayList<Bound> { // The collection of bounds for an area.
108 public Area() {
109 super();
110 }
111 }
112
113 public class AttItem {
114 public Conv conv;
115 public Object val;
116
117 AttItem(Conv iconv, Object ival) {
118 conv = iconv;
119 val = ival;
120 }
121 }
122
123 public class AttMap extends EnumMap<Att, AttItem> {
124 public AttMap() {
125 super(Att.class);
126 }
127 }
128
129 public class ObjTab extends HashMap<Integer, AttMap> {
130 public ObjTab() {
131 super();
132 }
133 }
134
135 public class ObjMap extends EnumMap<Obj, ObjTab> {
136 public ObjMap() {
137 super(Obj.class);
138 }
139 }
140
141 public class NodeTab extends HashMap<Long, Snode> {
142 public NodeTab() {
143 super();
144 }
145 }
146
147 public class DpthTab extends HashMap<Long, Snode> {
148 public DpthTab() {
149 super();
150 }
151 }
152
153 public class EdgeTab extends HashMap<Long, Edge> {
154 public EdgeTab() {
155 super();
156 }
157 }
158
159 public class AreaTab extends HashMap<Long, Area> {
160 public AreaTab() {
161 super();
162 }
163 }
164
165 public class FtrMap extends EnumMap<Obj, ArrayList<Feature>> {
166 public FtrMap() {
167 super(Obj.class);
168 }
169 }
170
171 public class FtrTab extends HashMap<Long, Feature> {
172 public FtrTab() {
173 super();
174 }
175 }
176
177 public enum Fflag {
178 UNKN, POINT, LINE, AREA
179 }
180
181 public class Feature {
182 public Fflag flag;
183 public long refs;
184 public Obj type;
185 public AttMap atts;
186 public ObjMap objs;
187 public double area;
188 public double length;
189 public Snode centre;
190
191 Feature() {
192 flag = Fflag.UNKN;
193 refs = 0;
194 type = Obj.UNKOBJ;
195 atts = new AttMap();
196 objs = new ObjMap();
197 area = 0;
198 length = 0;
199 centre = new Snode();
200 }
201 }
202
203 public NodeTab nodes;
204 public DpthTab depths;
205 public EdgeTab edges;
206 public AreaTab areas;
207
208 public FtrMap features;
209 public FtrTab index;
210
211 private Feature feature;
212 private Edge edge;
213 private ArrayList<Long> outers;
214 private ArrayList<Long> inners;
215
216 public class EdgeIterator {
217 Edge edge;
218 boolean forward;
219 ListIterator<Long> it;
220
221 public EdgeIterator(Edge iedge, boolean dir) {
222 edge = iedge;
223 forward = dir;
224 it = null;
225 }
226
227 public boolean hasNext() {
228 return (edge != null);
229 }
230
231 public Snode next() {
232 long ref = 0;
233 if (forward) {
234 if (it == null) {
235 ref = edge.first;
236 it = edge.nodes.listIterator();
237 } else {
238 if (it.hasNext()) {
239 ref = it.next();
240 } else {
241 ref = edge.last;
242 edge = null;
243 }
244 }
245 } else {
246 if (it == null) {
247 ref = edge.last;
248 it = edge.nodes.listIterator(edge.nodes.size());
249 } else {
250 if (it.hasPrevious()) {
251 ref = it.previous();
252 } else {
253 ref = edge.first;
254 edge = null;
255 }
256 }
257 }
258 return nodes.get(ref);
259 }
260 }
261
262 public class BoundIterator {
263 Bound bound;
264 Side side;
265 ListIterator<Side> sit;
266 EdgeIterator eit;
267
268 public BoundIterator(Bound ibound) {
269 bound = ibound;
270 sit = bound.sides.listIterator();
271 if (sit.hasNext()) {
272 side = sit.next();
273 eit = new EdgeIterator(side.edge, side.forward);
274 } else {
275 side = null;
276 }
277 }
278
279 public boolean hasNext() {
280 return (side != null) && ((sit.hasNext()) || (eit.hasNext()));
281 }
282
283 public Snode next() {
284 Snode node = null;
285 if (side != null) {
286 if (eit.hasNext()) {
287 node = eit.next();
288 } else {
289 if (sit.hasNext()) {
290 side = sit.next();
291 eit = new EdgeIterator(side.edge, side.forward);
292 node = eit.next();
293 } else {
294 side = null;
295 }
296 }
297 }
298 return node;
299 }
300 }
301
302 public SeaMap() {
303 nodes = new NodeTab();
304 edges = new EdgeTab();
305 areas = new AreaTab();
306 feature = new Feature();
307 features = new FtrMap();
308 index = new FtrTab();
309 }
310
311 public void addNode(long id, double lat, double lon) {
312 nodes.put(id, new Snode(Math.toRadians(lat), Math.toRadians(lon)));
313 feature = new Feature();
314 feature.refs = id;
315 feature.flag = Fflag.POINT;
316 edge = null;
317 }
318
319 public void addEdge(long id) {
320 feature = new Feature();
321 feature.refs = id;
322 feature.flag = Fflag.LINE;
323 edge = new Edge();
324 }
325
326 public void addToEdge(long node) {
327 if (edge.first == 0) {
328 edge.first = node;
329 } else {
330 if (edge.last != 0) {
331 edge.nodes.add(edge.last);
332 }
333 edge.last = node;
334 }
335 }
336
337 public void addArea(long id) {
338 feature = new Feature();
339 feature.refs = id;
340 feature.flag = Fflag.AREA;
341 outers = new ArrayList<Long>();
342 inners = new ArrayList<Long>();
343 edge = null;
344 }
345
346 public void addToArea(long id, boolean outer) {
347 if (outer) {
348 outers.add(id);
349 } else {
350 inners.add(id);
351 }
352 }
353
354 public void addTag(String key, String val) {
355 String subkeys[] = key.split(":");
356 if ((subkeys.length > 1) && subkeys[0].equals("seamark")) {
357 Obj obj = S57obj.enumType(subkeys[1]);
358 if ((subkeys.length > 2) && (obj != Obj.UNKOBJ)) {
359 int idx = 0;
360 Att att = Att.UNKATT;
361 try {
362 idx = Integer.parseInt(subkeys[2]);
363 if (subkeys.length == 4) {
364 att = s57.S57att.enumAttribute(subkeys[3], obj);
365 }
366 } catch (Exception e) {
367 att = S57att.enumAttribute(subkeys[2], obj);
368 }
369 ObjTab items = feature.objs.get(obj);
370 if (items == null) {
371 items = new ObjTab();
372 feature.objs.put(obj, items);
373 }
374 AttMap atts = items.get(idx);
375 if (atts == null) {
376 atts = new AttMap();
377 items.put(idx, atts);
378 }
379 AttVal<?> attval = S57val.convertValue(val, att);
380 if (attval.val != null)
381 atts.put(att, new AttItem(attval.conv, attval.val));
382 } else {
383 if (subkeys[1].equals("type")) {
384 feature.type = S57obj.enumType(val);
385 if (feature.objs.get(feature.type) == null) {
386 feature.objs.put(feature.type, new ObjTab());
387 }
388 } else {
389 Att att = S57att.enumAttribute(subkeys[1], Obj.UNKOBJ);
390 if (att != Att.UNKATT) {
391 AttVal<?> attval = S57val.convertValue(val, att);
392 if (attval.val != null)
393 feature.atts.put(att, new AttItem(attval.conv, attval.val));
394 }
395 }
396 }
397 }
398 }
399
400 public void tagsDone(long id) {
401 switch (feature.flag) {
402 case POINT:
403 Snode node = nodes.get(id);
404 if (node.flg != Nflag.CONN) {
405 node.flg = Nflag.ISOL;
406 }
407 feature.length = 0;
408 feature.area = 0;
409 break;
410 case LINE:
411 edges.put(id, edge);
412 nodes.get(edge.first).flg = Nflag.CONN;
413 nodes.get(edge.last).flg = Nflag.CONN;
414 Bound ebound = (new Bound(new Side(edge, true), true));
415 feature.length = calcLength(ebound);
416 if (edge.first == edge.last) {
417 feature.flag = Fflag.AREA;
418 Area area = new Area();
419 area.add(ebound);
420 feature.area = calcArea(ebound);
421 areas.put(id, area);
422 } else {
423 feature.area = 0;
424 }
425 break;
426 case AREA:
427 Bound bound = null;
428 Area area = new Area();
429 ArrayList<Long> role = outers;
430 while (role != null) {
431 while (!role.isEmpty()) {
432 Edge edge = edges.get(role.remove(0));
433 long node1 = edge.first;
434 long node2 = edge.last;
435 bound = new Bound(new Side(edge, true), (role == outers));
436 if (node1 != node2) {
437 for (ListIterator<Long> it = role.listIterator(0); it.hasNext();) {
438 Edge nedge = edges.get(it.next());
439 if (nedge.first == node2) {
440 bound.sides.add(new Side(nedge, true));
441 it.remove();
442 if (nedge.last == node2)
443 break;
444 } else if (nedge.last == node2) {
445 bound.sides.add(new Side(nedge, false));
446 it.remove();
447 if (nedge.first == node2)
448 break;
449 }
450 }
451 }
452 area.add(bound);
453 }
454 if (role == outers) {
455 feature.length = calcLength(bound);
456 feature.area = calcArea(bound);
457 role = inners;
458 } else {
459 role = null;
460 }
461 }
462 areas.put(id, area);
463 break;
464 default:
465 break;
466 }
467 if ((feature.type != Obj.UNKOBJ) && !((edge != null) && (edge.last == 0))) {
468 index.put(id, feature);
469 if (features.get(feature.type) == null) {
470 features.put(feature.type, new ArrayList<Feature>());
471 }
472 feature.centre = findCentroid(feature);
473 features.get(feature.type).add(feature);
474 }
475 }
476
477 double signedArea(Bound bound) {
478 Snode node;
479 double lat, lon, llon, llat;
480 lat = lon = llon = llat = 0;
481 double sigma = 0;
482 BoundIterator it = new BoundIterator(bound);
483 while (it.hasNext()) {
484 llon = lon;
485 llat = lat;
486 node = it.next();
487 lat = node.lat;
488 lon = node.lon;
489 sigma += (lon * Math.sin(llat)) - (llon * Math.sin(lat));
490 }
491 return sigma / 2.0;
492 }
493
494 public boolean handOfArea(Bound bound) {
495 return (signedArea(bound) < 0);
496 }
497
498 public double calcArea(Bound bound) {
499 return Math.abs(signedArea(bound)) * 3444 * 3444;
500 }
501
502 public double calcLength(Bound bound) {
503 Snode node;
504 double lat, lon, llon, llat;
505 lat = lon = llon = llat = 0;
506 double sigma = 0;
507 BoundIterator it = new BoundIterator(bound);
508 if (it.hasNext()) {
509 node = it.next();
510 lat = node.lat;
511 lon = node.lon;
512 while (it.hasNext()) {
513 llon = lon;
514 llat = lat;
515 node = it.next();
516 lat = node.lat;
517 lon = node.lon;
518 sigma += Math.acos(Math.sin(lat) * Math.sin(llat) + Math.cos(lat) * Math.cos(llat) * Math.cos(llon - lon));
519 }
520 }
521 return sigma * 3444;
522 }
523
524 public Snode findCentroid(Feature feature) {
525 double lat, lon, slat, slon, llat, llon;
526 llat = llon = lat = lon = slat = slon = 0;
527 double sarc = 0;
528 boolean first = true;
529 switch (feature.flag) {
530 case POINT:
531 return nodes.get(feature.refs);
532 case LINE:
533 Edge edge = edges.get(feature.refs);
534 EdgeIterator eit = new EdgeIterator(edge, true);
535 while (eit.hasNext()) {
536 Snode node = eit.next();
537 lat = node.lat;
538 lon = node.lon;
539 if (first) {
540 first = false;
541 } else {
542 sarc += (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
543 }
544 llat = lat;
545 llon = lon;
546 }
547 double harc = sarc / 2;
548 sarc = 0;
549 first = true;
550 eit = new EdgeIterator(edge, true);
551 while (eit.hasNext()) {
552 Snode node = eit.next();
553 lat = node.lat;
554 lon = node.lon;
555 if (first) {
556 first = false;
557 } else {
558 sarc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
559 if (sarc > harc)
560 break;
561 }
562 harc -= sarc;
563 llat = lat;
564 llon = lon;
565 }
566 return new Snode(llat + ((lat - llat) * harc / sarc), llon + ((lon - llon) * harc / sarc));
567 case AREA:
568 Bound bound = areas.get(feature.refs).get(0);
569 BoundIterator bit = new BoundIterator(bound);
570 while (bit.hasNext()) {
571 Snode node = bit.next();
572 lat = node.lat;
573 lon = node.lon;
574 if (first) {
575 first = false;
576 } else {
577 double arc = (Math.acos(Math.cos(lon - llon) * Math.cos(lat - llat)));
578 slat += ((lat + llat) / 2 * arc);
579 slon += ((lon + llon) / 2 * arc);
580 sarc += arc;
581 }
582 llon = lon;
583 llat = lat;
584 }
585 return new Snode((sarc > 0.0 ? slat / sarc : 0.0), (sarc > 0.0 ? slon / sarc : 0.0));
586 default:
587 break;
588 }
589 return null;
590 }
591
592}
Note: See TracBrowser for help on using the repository browser.