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

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

save

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