6 package com.google.appinventor.components.runtime.util;
8 import android.text.TextUtils;
9 import android.util.Log;
21 import com.
google.common.annotations.VisibleForTesting;
22 import gnu.lists.FString;
23 import gnu.lists.LList;
24 import gnu.lists.Pair;
25 import org.json.JSONArray;
26 import org.json.JSONException;
27 import org.json.JSONObject;
28 import org.osmdroid.util.GeoPoint;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.PrintStream;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
47 private static final java.util.Map<String, Integer> colors;
48 private static final int ERROR_CODE_MALFORMED_GEOJSON = -3;
49 private static final String ERROR_MALFORMED_GEOJSON =
"Malformed GeoJSON response. Expected FeatureCollection as root element.";
50 private static final String ERROR_UNKNOWN_TYPE =
"Unrecognized/invalid type in JSON object";
51 private static final String GEOJSON_COORDINATES =
"coordinates";
52 private static final String GEOJSON_FEATURE =
"Feature";
53 private static final String GEOJSON_FEATURECOLLECTION =
"FeatureCollection";
54 private static final String GEOJSON_FEATURES =
"features";
55 private static final String GEOJSON_GEOMETRY =
"geometry";
56 private static final String GEOJSON_GEOMETRYCOLLECTION =
"GeometryCollection";
57 private static final String GEOJSON_PROPERTIES =
"properties";
58 private static final String GEOJSON_TYPE =
"type";
59 private static final String PROPERTY_ANCHOR_HORIZONTAL =
"anchorHorizontal";
60 private static final String PROPERTY_ANCHOR_VERTICAL =
"anchorVertical";
61 private static final String PROPERTY_DESCRIPTION =
"description";
62 private static final String PROPERTY_DRAGGABLE =
"draggable";
63 private static final String PROPERTY_FILL =
"fill";
64 private static final String PROPERTY_FILL_OPACITY =
"fill-opacity";
65 private static final String PROPERTY_HEIGHT =
"height";
66 private static final String PROPERTY_IMAGE =
"image";
67 private static final String PROPERTY_INFOBOX =
"infobox";
68 private static final String PROPERTY_STROKE =
"stroke";
69 private static final String PROPERTY_STROKE_OPACITY =
"stroke-opacity";
70 private static final String PROPERTY_STROKE_WIDTH =
"stroke-width";
71 private static final String PROPERTY_TITLE =
"title";
72 private static final String PROPERTY_WIDTH =
"width";
73 private static final String PROPERTY_VISIBLE =
"visible";
74 private static final int KEY = 1;
75 private static final int VALUE = 2;
77 private static final int LATITUDE = 2;
78 private static final int LONGITUDE = 1;
81 private interface PropertyApplication {
86 colors =
new HashMap<String, Integer>();
87 colors.put(
"black", COLOR_BLACK);
88 colors.put(
"blue", COLOR_BLUE);
89 colors.put(
"cyan", COLOR_CYAN);
90 colors.put(
"darkgray", COLOR_DKGRAY);
91 colors.put(
"gray", COLOR_GRAY);
92 colors.put(
"green", COLOR_GREEN);
93 colors.put(
"lightgray", COLOR_LTGRAY);
94 colors.put(
"magenta", COLOR_MAGENTA);
95 colors.put(
"orange", COLOR_ORANGE);
96 colors.put(
"pink", COLOR_PINK);
97 colors.put(
"red", COLOR_RED);
98 colors.put(
"white", COLOR_WHITE);
99 colors.put(
"yellow", COLOR_YELLOW);
101 SUPPORTED_PROPERTIES =
new HashMap<String, PropertyApplication>();
102 SUPPORTED_PROPERTIES.put(PROPERTY_ANCHOR_HORIZONTAL.toLowerCase(),
new PropertyApplication() {
104 public void apply(
MapFeature feature, Object value) {
106 ((
MapMarker) feature).AnchorHorizontal(parseIntegerOrString(value));
110 SUPPORTED_PROPERTIES.put(PROPERTY_ANCHOR_VERTICAL.toLowerCase(),
new PropertyApplication() {
112 public void apply(
MapFeature feature, Object value) {
114 ((
MapMarker) feature).AnchorHorizontal();
118 SUPPORTED_PROPERTIES.put(PROPERTY_DESCRIPTION,
new PropertyApplication() {
120 public void apply(
MapFeature feature, Object value) {
124 SUPPORTED_PROPERTIES.put(PROPERTY_DRAGGABLE,
new PropertyApplication() {
126 public void apply(
MapFeature feature, Object value) {
127 feature.
Draggable(parseBooleanOrString(value));
130 SUPPORTED_PROPERTIES.put(PROPERTY_FILL,
new PropertyApplication() {
132 public void apply(
MapFeature feature, Object value) {
133 if (feature instanceof
HasFill) {
134 ((
HasFill) feature).FillColor(value instanceof Number ? ((Number) value).intValue() :
135 parseColor(value.toString()));
139 SUPPORTED_PROPERTIES.put(PROPERTY_FILL_OPACITY,
new PropertyApplication() {
141 public void apply(
MapFeature feature, Object value) {
142 if (feature instanceof
HasFill) {
143 ((
HasFill) feature).FillOpacity(parseFloatOrString(value));
147 SUPPORTED_PROPERTIES.put(PROPERTY_HEIGHT,
new PropertyApplication() {
149 public void apply(
MapFeature feature, Object value) {
151 ((
MapMarker) feature).Height(parseIntegerOrString(value));
155 SUPPORTED_PROPERTIES.put(PROPERTY_IMAGE,
new PropertyApplication() {
157 public void apply(
MapFeature feature, Object value) {
159 ((
MapMarker) feature).ImageAsset(value.toString());
163 SUPPORTED_PROPERTIES.put(PROPERTY_INFOBOX,
new PropertyApplication() {
165 public void apply(
MapFeature feature, Object value) {
169 SUPPORTED_PROPERTIES.put(PROPERTY_STROKE,
new PropertyApplication() {
171 public void apply(
MapFeature feature, Object value) {
173 ((
HasStroke) feature).StrokeColor(value instanceof Number ? ((Number) value).intValue() :
174 parseColor(value.toString()));
178 SUPPORTED_PROPERTIES.put(PROPERTY_STROKE_OPACITY,
new PropertyApplication() {
180 public void apply(
MapFeature feature, Object value) {
182 ((
HasStroke) feature).StrokeOpacity(parseFloatOrString(value));
186 SUPPORTED_PROPERTIES.put(PROPERTY_STROKE_WIDTH,
new PropertyApplication() {
188 public void apply(
MapFeature feature, Object value) {
190 ((
HasStroke) feature).StrokeWidth(parseIntegerOrString(value));
194 SUPPORTED_PROPERTIES.put(PROPERTY_TITLE,
new PropertyApplication() {
196 public void apply(
MapFeature feature, Object value) {
197 feature.
Title(value.toString());
200 SUPPORTED_PROPERTIES.put(PROPERTY_WIDTH,
new PropertyApplication() {
202 public void apply(
MapFeature feature, Object value) {
204 ((
MapMarker) feature).Width(parseIntegerOrString(value));
208 SUPPORTED_PROPERTIES.put(PROPERTY_VISIBLE,
new PropertyApplication() {
210 public void apply(
MapFeature feature, Object value) {
211 feature.
Visible(parseBooleanOrString(value));
219 static int parseColor(
final String value) {
220 String lcValue = value.toLowerCase();
221 Integer result = colors.get(lcValue);
222 if (result !=
null) {
224 }
else if (lcValue.startsWith(
"#")) {
225 return parseColorHex(lcValue.substring(1));
226 }
else if (lcValue.startsWith(
"&h")) {
227 return parseColorHex(lcValue.substring(2));
234 static int parseColorHex(
final String value) {
236 if (value.length() == 3) {
240 for (
int i = 0; i < value.length(); i++) {
241 hex = charToHex(value.charAt(i));
242 argb |= ((hex << 4) | hex) << (2-i)*8;
244 }
else if (value.length() == 6) {
248 for (
int i = 0; i < 3; i++) {
249 hex = charToHex(value.charAt(2*i)) << 4 | charToHex(value.charAt(2*i+1));
250 argb |= hex << (2-i)*8;
252 }
else if (value.length() == 8) {
255 for (
int i = 0; i < 4; i++) {
256 hex = charToHex(value.charAt(2*i)) << 4 | charToHex(value.charAt(2*i+1));
257 argb |= hex << (3-i)*8;
260 throw new IllegalArgumentException();
266 static int charToHex(
char c) {
267 if (
'0' <= c && c <=
'9' ) {
269 }
else if (
'a' <= c && c <=
'f' ) {
271 }
else if (
'A' <= c && c <=
'F' ) {
274 throw new IllegalArgumentException(
"Invalid hex character. Expected [0-9A-Fa-f].");
283 for (Object o : (LList) descriptions.getCdr()) {
287 if (GEOJSON_TYPE.equals(key)) {
288 type = (String) value;
289 }
else if (GEOJSON_GEOMETRY.equals(key)) {
291 }
else if (GEOJSON_PROPERTIES.equals(key)) {
294 Log.w(logTag, String.format(
"Unsupported field \"%s\" in JSON format", key));
297 if (!GEOJSON_FEATURE.equals(type)) {
298 throw new IllegalArgumentException(String.format(
"Unknown type \"%s\"", type));
300 if (geometry ==
null) {
301 throw new IllegalArgumentException(
"No geometry defined for feature.");
304 if (properties !=
null) {
305 processProperties(logTag, feature, properties);
314 for (Object o : (LList) geometry.getCdr()) {
318 if (GEOJSON_TYPE.equals(key)) {
319 type = (String) value;
320 }
else if (GEOJSON_COORDINATES.equals(key)) {
323 Log.w(logTag, String.format(
"Unsupported field \"%s\" in JSON format", key));
326 if (coordinates ==
null) {
327 throw new IllegalArgumentException(
"No coordinates found in GeoJSON Feature");
329 return processCoordinates(container, type, coordinates);
332 private static MapFeature processCoordinates(
final MapFeatureContainer container,
333 final String type,
final YailList coordinates) {
334 if (MapFactory.MapFeatureType.TYPE_POINT.equals(type)) {
335 return markerFromGeoJSON(container, coordinates);
336 }
else if (MapFactory.MapFeatureType.TYPE_LINESTRING.equals(type)) {
337 return lineStringFromGeoJSON(container, coordinates);
338 }
else if (MapFactory.MapFeatureType.TYPE_POLYGON.equals(type)) {
339 return polygonFromGeoJSON(container, coordinates);
340 }
else if (MapFeatureType.TYPE_MULTIPOLYGON.equals(type)) {
341 return multipolygonFromGeoJSON(container, coordinates);
343 throw new IllegalArgumentException();
346 private static MapFactory.MapMarker markerFromGeoJSON(
final MapFeatureContainer container,
347 final YailList coordinates) {
348 if (coordinates.length() != 3) {
349 throw new IllegalArgumentException(
"Invalid coordinate supplied in GeoJSON");
351 Marker marker =
new Marker(container);
352 marker.Latitude(((Number) coordinates.get(LATITUDE)).doubleValue());
353 marker.Longitude(((Number) coordinates.get(LONGITUDE)).doubleValue());
357 private static MapLineString lineStringFromGeoJSON(
final MapFeatureContainer container,
358 final YailList coordinates) {
359 if (coordinates.size() < 2) {
360 throw new IllegalArgumentException(
"Too few coordinates supplied in GeoJSON");
362 LineString lineString =
new LineString(container);
367 private static MapPolygon polygonFromGeoJSON(
final MapFeatureContainer container,
368 final YailList coordinates) {
369 Polygon polygon =
new Polygon(container);
370 Iterator i = coordinates.iterator();
374 polygon.HolePoints(YailList.makeList(
swapNestedCoordinates((LList) ((Pair)coordinates.getCdr()).getCdr())));
376 polygon.Initialize();
380 private static MapPolygon multipolygonFromGeoJSON(
final MapFeatureContainer container,
381 final YailList coordinates) {
382 Polygon polygon =
new Polygon(container);
383 List<YailList> points =
new ArrayList<YailList>();
384 List<YailList> holePoints =
new ArrayList<YailList>();
385 Iterator i = coordinates.iterator();
387 while (i.hasNext()) {
388 YailList list = (YailList) i.next();
392 polygon.Points(YailList.makeList(points));
393 polygon.HolePoints(YailList.makeList(holePoints));
394 polygon.Initialize();
398 private static void processProperties(
final String logTag,
final MapFactory.MapFeature feature,
399 final YailList properties) {
400 for (Object o : properties) {
401 if (o instanceof YailList) {
402 YailList pair = (YailList) o;
403 String key = pair.get(KEY).toString();
404 PropertyApplication application = SUPPORTED_PROPERTIES.get(key.toLowerCase());
405 if (application !=
null) {
406 application.apply(feature, pair.get(VALUE));
408 Log.i(logTag, String.format(
"Ignoring GeoJSON property \"%s\"", key));
415 static boolean parseBooleanOrString(Object value) {
416 if (value instanceof Boolean) {
417 return (Boolean) value;
418 }
else if (value instanceof String) {
419 return !(
"false".equalsIgnoreCase((String) value) || ((String) value).length() == 0);
420 }
else if (value instanceof FString) {
421 return parseBooleanOrString(value.toString());
423 throw new IllegalArgumentException();
428 static int parseIntegerOrString(Object value) {
429 if (value instanceof Number) {
430 return ((Number) value).intValue();
431 }
else if (value instanceof String) {
432 return Integer.parseInt((String) value);
433 }
else if (value instanceof FString) {
434 return Integer.parseInt(value.toString());
436 throw new IllegalArgumentException();
441 static float parseFloatOrString(Object value) {
442 if (value instanceof Number) {
443 return ((Number) value).floatValue();
444 }
else if (value instanceof String) {
445 return Float.parseFloat((String) value);
446 }
else if (value instanceof FString) {
447 return Float.parseFloat(value.toString());
449 throw new IllegalArgumentException();
453 public static List<YailList>
getGeoJSONFeatures(
final String logTag,
final String content)
throws JSONException {
454 JSONObject parsedData =
new JSONObject(stripBOM(content));
455 JSONArray features = parsedData.getJSONArray(GEOJSON_FEATURES);
456 List<YailList> yailFeatures =
new ArrayList<YailList>();
457 for (
int i = 0; i < features.length(); i++) {
458 yailFeatures.add(jsonObjectToYail(logTag, features.getJSONObject(i)));
463 public static String
getGeoJSONType(
final String content,
final String geojsonType)
throws JSONException {
464 JSONObject parsedData =
new JSONObject(stripBOM(content));
465 String type = parsedData.optString(geojsonType);
469 private static YailList jsonObjectToYail(
final String logTag,
final JSONObject
object)
throws JSONException {
470 List<YailList> pairs =
new ArrayList<YailList>();
471 @SuppressWarnings(
"unchecked")
472 Iterator<String> j =
object.keys();
473 while (j.hasNext()) {
474 String key = j.next();
475 Object value =
object.get(key);
476 if (value instanceof Boolean ||
477 value instanceof Integer ||
478 value instanceof Long ||
479 value instanceof Double ||
480 value instanceof String) {
482 }
else if (value instanceof JSONArray) {
483 pairs.add(
YailList.
makeList(
new Object[] { key, jsonArrayToYail(logTag, (JSONArray) value)}));
484 }
else if (value instanceof JSONObject) {
485 pairs.add(YailList.makeList(
new Object[] { key, jsonObjectToYail(logTag, (JSONObject) value)}));
486 }
else if (!JSONObject.NULL.equals(value)) {
487 Log.wtf(logTag, ERROR_UNKNOWN_TYPE +
": " + value.getClass());
488 throw new IllegalArgumentException(ERROR_UNKNOWN_TYPE);
491 return YailList.makeList(pairs);
494 private static YailList jsonArrayToYail(
final String logTag,
final JSONArray array)
throws JSONException {
495 List<Object> items =
new ArrayList<Object>();
496 for (
int i = 0; i < array.length(); i++) {
497 Object value = array.get(i);
498 if (value instanceof Boolean ||
499 value instanceof Integer ||
500 value instanceof Long ||
501 value instanceof Double ||
502 value instanceof String) {
504 }
else if (value instanceof JSONArray) {
505 items.add(jsonArrayToYail(logTag, (JSONArray) value));
506 }
else if (value instanceof JSONObject) {
507 items.add(jsonObjectToYail(logTag, (JSONObject) value));
508 }
else if (!JSONObject.NULL.equals(value)) {
509 Log.wtf(logTag, ERROR_UNKNOWN_TYPE +
": " + value.getClass());
510 throw new IllegalArgumentException(ERROR_UNKNOWN_TYPE);
513 return YailList.makeList(items);
516 private static String stripBOM(String content) {
517 if (content.charAt(0) ==
'\uFEFF') {
518 return content.substring(1);
524 private static final class FeatureWriter
implements MapFactory.MapFeatureVisitor<Void> {
526 private final PrintStream out;
528 private FeatureWriter(PrintStream out) {
532 private void writeType(String type) {
533 out.print(
"\"type\":\"");
538 private void writeProperty(String property, Object value) {
540 String result = JsonUtil.getJsonRepresentation(value);
545 }
catch(JSONException e) {
546 Log.w(
"GeoJSONUtil",
"Unable to serialize the value of \"" + property +
"\" as JSON", e);
550 private void writeProperty(String property, String value) {
551 if (value ==
null || TextUtils.isEmpty(value)) {
555 writeProperty(property, (Object) value);
558 private void writeColorProperty(String property,
int color) {
561 out.print(
"\":\"&H");
562 String unpadded = Integer.toHexString(color);
563 for (
int i = 8; i > unpadded.length(); i--) {
570 private void writePointGeometry(GeoPoint point) {
571 out.print(
"\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
572 out.print(point.getLongitude());
574 out.print(point.getLatitude());
575 if (hasAltitude(point)) {
577 out.print(point.getAltitude());
582 private void writePropertiesHeader(String runtimeType) {
583 out.print(
",\"properties\":{\"$Type\":\"" + runtimeType +
"\"");
586 private void writeProperties(MapFeature feature) {
587 writeProperty(PROPERTY_DESCRIPTION, feature.Description());
588 writeProperty(PROPERTY_DRAGGABLE, feature.Draggable());
589 writeProperty(PROPERTY_INFOBOX, feature.EnableInfobox());
590 writeProperty(PROPERTY_TITLE, feature.Title());
591 writeProperty(PROPERTY_VISIBLE, feature.Visible());
594 private void writeProperties(HasStroke feature) {
595 writeColorProperty(PROPERTY_STROKE, feature.StrokeColor());
596 writeProperty(PROPERTY_STROKE_OPACITY, feature.StrokeOpacity());
597 writeProperty(PROPERTY_STROKE_WIDTH, feature.StrokeWidth());
600 private void writeProperties(HasFill feature) {
601 writeColorProperty(PROPERTY_FILL, feature.FillColor());
602 writeProperty(PROPERTY_FILL_OPACITY, feature.FillOpacity());
605 private void writePoints(List<GeoPoint> points) {
606 boolean first =
true;
607 for (GeoPoint p : points) {
608 if (!first) out.print(
',');
610 out.print(p.getLongitude());
612 out.print(p.getLatitude());
613 if (hasAltitude(p)) {
615 out.print(p.getAltitude());
622 private void writeLineGeometry(MapLineString lineString) {
623 out.print(
"\"geometry\":{\"type\":\"LineString\",\"coordinates\":[");
624 writePoints(lineString.getPoints());
628 private void writeMultipolygonGeometryNoHoles(MapPolygon polygon) {
629 out.print(
"\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[");
630 Iterator<List<GeoPoint>> pointIterator = polygon.getPoints().iterator();
631 Iterator<List<List<GeoPoint>>> holePointIterator = polygon.getHolePoints().iterator();
632 boolean first =
true;
633 while (pointIterator.hasNext()) {
634 if (!first) out.print(
",");
636 writePoints(pointIterator.next());
637 if (holePointIterator.hasNext()) {
638 for (List<GeoPoint> holePoints : holePointIterator.next()) {
640 writePoints(holePoints);
649 private void writePolygonGeometryNoHoles(MapPolygon polygon) {
650 out.print(
"\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[");
651 writePoints(polygon.getPoints().get(0));
652 if (!polygon.getHolePoints().isEmpty()) {
653 for (List<GeoPoint> points : polygon.getHolePoints().get(0)) {
661 private void writePolygonGeometry(MapPolygon polygon) {
662 if (polygon.getPoints().size() > 1) {
663 writeMultipolygonGeometryNoHoles(polygon);
665 writePolygonGeometryNoHoles(polygon);
670 public Void
visit(MapFactory.MapMarker marker, Object... arguments) {
672 writeType(GEOJSON_FEATURE);
674 writePointGeometry(marker.getCentroid());
675 writePropertiesHeader(marker.getClass().getName());
676 writeProperties((MapFeature) marker);
677 writeProperties((HasStroke) marker);
678 writeProperties((HasFill) marker);
679 writeProperty(PROPERTY_ANCHOR_HORIZONTAL, marker.AnchorHorizontal());
680 writeProperty(PROPERTY_ANCHOR_VERTICAL, marker.AnchorVertical());
681 writeProperty(PROPERTY_HEIGHT, marker.Height());
682 writeProperty(PROPERTY_IMAGE, marker.ImageAsset());
683 writeProperty(PROPERTY_WIDTH, marker.Width());
689 public Void
visit(MapFactory.MapLineString lineString, Object... arguments) {
691 writeType(GEOJSON_FEATURE);
693 writeLineGeometry(lineString);
694 writePropertiesHeader(lineString.getClass().getName());
695 writeProperties((MapFeature) lineString);
696 writeProperties((HasStroke) lineString);
702 public Void
visit(MapFactory.MapPolygon polygon, Object... arguments) {
704 writeType(GEOJSON_FEATURE);
706 writePolygonGeometry(polygon);
707 writePropertiesHeader(polygon.getClass().getName());
708 writeProperties((MapFeature) polygon);
709 writeProperties((HasStroke) polygon);
710 writeProperties((HasFill) polygon);
716 public Void
visit(MapFactory.MapCircle circle, Object... arguments) {
718 writeType(GEOJSON_FEATURE);
720 writePointGeometry(circle.getCentroid());
721 writePropertiesHeader(circle.getClass().getName());
722 writeProperties((MapFeature) circle);
723 writeProperties((HasStroke) circle);
724 writeProperties((HasFill) circle);
730 public Void
visit(MapFactory.MapRectangle rectangle, Object... arguments) {
732 writeType(GEOJSON_FEATURE);
733 out.print(
",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[");
734 out.print(
"[" + rectangle.WestLongitude() +
"," + rectangle.NorthLatitude() +
"],");
735 out.print(
"[" + rectangle.WestLongitude() +
"," + rectangle.SouthLatitude() +
"],");
736 out.print(
"[" + rectangle.EastLongitude() +
"," + rectangle.SouthLatitude() +
"],");
737 out.print(
"[" + rectangle.EastLongitude() +
"," + rectangle.NorthLatitude() +
"],");
738 out.print(
"[" + rectangle.WestLongitude() +
"," + rectangle.NorthLatitude() +
"]]}");
739 writePropertiesHeader(rectangle.getClass().getName());
740 writeProperties((MapFeature) rectangle);
741 writeProperties((HasStroke) rectangle);
742 writeProperties((HasFill) rectangle);
743 writeProperty(
"NorthLatitude", rectangle.NorthLatitude());
744 writeProperty(
"WestLongitude", rectangle.WestLongitude());
745 writeProperty(
"SouthLatitude", rectangle.SouthLatitude());
746 writeProperty(
"EastLongitude", rectangle.EastLongitude());
755 private static boolean hasAltitude(GeoPoint point) {
756 return Double.compare(0.0, point.getAltitude()) != 0;
761 PrintStream out =
null;
763 out =
new PrintStream(
new FileOutputStream(path));
764 FeatureWriter writer =
new FeatureWriter(out);
765 out.print(
"{\"type\": \"FeatureCollection\", \"features\":[");
767 Iterator<MapFeature> it = featuresToSave.iterator();
771 while (it.hasNext()) {
790 Iterator i = coordinates.
iterator();
792 while (i.hasNext()) {
794 Object temp = coordinate.get(1);
795 Pair p = (Pair) coordinate.getCdr();
796 p.setCar(coordinate.get(2));
797 p = (Pair) p.getCdr();
804 for (List<E> point : coordinates) {
805 E temp = point.get(0);
806 point.set(0, point.get(1));
813 LList it = coordinates;
814 while (!it.isEmpty()) {
816 it = (LList) ((Pair) it).getCdr();