1 /*
2  * Compass.java 18 mars 2010
3  *
4  * Sweet Home 3D, Copyright (c) 2006 Emmanuel PUYBARET / eTeks <info@eteks.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 package com.eteks.sweethome3d.model;
21 
22 import java.awt.geom.AffineTransform;
23 import java.awt.geom.Ellipse2D;
24 import java.awt.geom.PathIterator;
25 import java.awt.geom.Rectangle2D;
26 import java.lang.ref.WeakReference;
27 import java.util.Calendar;
28 import java.util.GregorianCalendar;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.TimeZone;
32 
33 /**
34  * A compass used to locate where a home is located and how it's oriented towards North.
35  * @since 3.0
36  * @author Emmanuel Puybaret
37  * @author Fr�d�ric Mantegazza (Sun location algorithm)
38  */
39 public class Compass extends HomeObject implements Selectable {
40   /**
41    * The properties of a compass that may change. <code>PropertyChangeListener</code>s added
42    * to a wall will be notified under a property name equal to the string value of one these properties.
43    */
44   public enum Property {X, Y, DIAMETER, VISIBLE, NORTH_DIRECTION, LATITUDE, LONGITUDE, TIME_ZONE}
45 
46   private static final long serialVersionUID = 1L;
47 
48   private float               x;
49   private float               y;
50   private float               diameter;
51   private boolean             visible;
52   private float               northDirection;
53   private float               latitude;
54   private float               longitude;
55   private TimeZone            timeZone;
56 
57   private transient float [][] pointsCache;
58   private transient Calendar   dateCache;
59   private transient float      sunElevationCache;
60   private transient float      sunAzimuthCache;
61 
62 
63   private static WeakReference<Map<String, GeographicPoint>> timeZoneGeographicPointsReference;
64 
65   /**
66    * Creates a compass drawn at the given point.
67    * North direction is set to zero, time zone to default
68    * and the latitude and the longitude of this new compass is equal
69    * to the geographic point matching the default time zone.
70    */
Compass(float x, float y, float diameter)71   public Compass(float x, float y, float diameter) {
72     this(createId("compass"), x, y, diameter);
73   }
74 
75   /**
76    * Creates a compass drawn at the given point.
77    * North direction is set to zero, time zone to default
78    * and the latitude and the longitude of this new compass is equal
79    * to the geographic point matching the default time zone.
80    * @since 6.4
81    */
Compass(String id, float x, float y, float diameter)82   public Compass(String id, float x, float y, float diameter) {
83     super(id);
84     this.x = x;
85     this.y = y;
86     this.diameter = diameter;
87     this.visible = true;
88     this.timeZone = TimeZone.getDefault();
89     initGeographicPoint();
90   }
91 
92   /**
93    * Returns the abscissa of the center of this compass.
94    */
getX()95   public float getX() {
96     return this.x;
97   }
98 
99   /**
100    * Sets the abscissa of the center of this compass. Once this compass is updated,
101    * listeners added to this compass will receive a change notification.
102    */
setX(float x)103   public void setX(float x) {
104     if (x != this.x) {
105       float oldX = this.x;
106       this.x = x;
107       this.pointsCache = null;
108       firePropertyChange(Property.X.name(), oldX, x);
109     }
110   }
111 
112   /**
113    * Returns the ordinate of the center of this compass.
114    */
getY()115   public float getY() {
116     return this.y;
117   }
118 
119   /**
120    * Sets the ordinate of the center of this compass. Once this compass is updated,
121    * listeners added to this compass will receive a change notification.
122    */
setY(float y)123   public void setY(float y) {
124     if (y != this.y) {
125       float oldY = this.y;
126       this.y = y;
127       this.pointsCache = null;
128       firePropertyChange(Property.Y.name(), oldY, y);
129     }
130   }
131 
132   /**
133    * Returns the diameter of this compass.
134    */
getDiameter()135   public float getDiameter() {
136     return this.diameter;
137   }
138 
139   /**
140    * Sets the diameter of this compass. Once this compass is updated,
141    * listeners added to this compass will receive a change notification.
142    */
setDiameter(float diameter)143   public void setDiameter(float diameter) {
144     if (diameter != this.diameter) {
145       float oldDiameter = this.diameter;
146       this.diameter = diameter;
147       this.pointsCache = null;
148       firePropertyChange(Property.DIAMETER.name(), oldDiameter, diameter);
149     }
150   }
151 
152   /**
153    * Returns <code>true</code> if this compass is visible.
154    */
isVisible()155   public boolean isVisible() {
156     return this.visible;
157   }
158 
159   /**
160    * Sets whether this compass is visible or not. Once this compass is updated,
161    * listeners added to this piece will receive a change notification.
162    */
setVisible(boolean visible)163   public void setVisible(boolean visible) {
164     if (visible != this.visible) {
165       this.visible = visible;
166       firePropertyChange(Property.VISIBLE.name(), !visible, visible);
167     }
168   }
169 
170   /**
171    * Returns the North direction angle of this compass in radians.
172    */
getNorthDirection()173   public float getNorthDirection() {
174     return this.northDirection;
175   }
176 
177   /**
178    * Sets the North direction angle of this compass. Once this compass is updated,
179    * listeners added to this compass will receive a change notification.
180    */
setNorthDirection(float northDirection)181   public void setNorthDirection(float northDirection) {
182     if (northDirection != this.northDirection) {
183       float oldNorthDirection = this.northDirection;
184       this.northDirection = northDirection;
185       this.pointsCache = null;
186       firePropertyChange(Property.NORTH_DIRECTION.name(), oldNorthDirection, northDirection);
187     }
188   }
189 
190   /**
191    * Returns the latitude of this compass in radians.
192    */
getLatitude()193   public final float getLatitude() {
194     return this.latitude;
195   }
196 
197   /**
198    * Sets the latitude of this compass. Once this compass is updated,
199    * listeners added to this compass will receive a change notification.
200    */
setLatitude(float latitude)201   public void setLatitude(float latitude) {
202     if (latitude != this.latitude) {
203       float oldLatitude = this.latitude;
204       this.latitude = latitude;
205       this.dateCache = null;
206       firePropertyChange(Property.LATITUDE.name(), oldLatitude, latitude);
207     }
208   }
209 
210   /**
211    * Returns the longitude of this compass in radians.
212    */
getLongitude()213   public final float getLongitude() {
214     return this.longitude;
215   }
216 
217   /**
218    * Sets the longitude of the center of this compass. Once this compass is updated,
219    * listeners added to this compass will receive a change notification.
220    */
setLongitude(float longitude)221   public void setLongitude(float longitude) {
222     if (longitude != this.longitude) {
223       float oldLongitude = this.longitude;
224       this.longitude = longitude;
225       this.dateCache = null;
226       firePropertyChange(Property.LONGITUDE.name(), oldLongitude, longitude);
227     }
228   }
229 
230   /**
231    * Returns the time zone identifier of this compass.
232    * @see java.util.TimeZone
233    */
getTimeZone()234   public String getTimeZone() {
235     return this.timeZone.getID();
236   }
237 
238   /**
239    * Sets the time zone identifier of this compass. Once this compass is updated,
240    * listeners added to this compass will receive a change notification.
241    * @throws IllegalArgumentException if <code>timeZone</code> is <code>null</code> or contains an unknown identifier.
242    * @see java.util.TimeZone
243    */
setTimeZone(String timeZone)244   public void setTimeZone(String timeZone) {
245     if (!this.timeZone.getID().equals(timeZone)) {
246       if (timeZone == null) {
247         throw new IllegalArgumentException("Time zone ID can't be null");
248       }
249       String oldTimeZone = this.timeZone.getID();
250       this.timeZone = TimeZone.getTimeZone(timeZone);
251       this.dateCache = null;
252       firePropertyChange(Property.TIME_ZONE.name(), oldTimeZone, timeZone);
253     }
254   }
255 
256   /**
257    * Returns the corner points of the square that contains compass disc.
258    */
getPoints()259   public float [][] getPoints() {
260     if (this.pointsCache == null) {
261       // Create the rectangle that matches piece bounds
262       Rectangle2D pieceRectangle = new Rectangle2D.Float(
263           getX() - getDiameter() / 2,
264           getY() - getDiameter() / 2,
265           getDiameter(), getDiameter());
266       // Apply rotation to the rectangle
267       AffineTransform rotation = AffineTransform.getRotateInstance(getNorthDirection(), getX(), getY());
268       this.pointsCache = new float[4][2];
269       PathIterator it = pieceRectangle.getPathIterator(rotation);
270       for (int i = 0; i < this.pointsCache.length; i++) {
271         it.currentSegment(this.pointsCache [i]);
272         it.next();
273       }
274     }
275     return new float [][] {this.pointsCache [0].clone(), this.pointsCache [1].clone(),
276                            this.pointsCache [2].clone(), this.pointsCache [3].clone()};
277   }
278 
279   /**
280    * Returns <code>true</code> if the disc of this compass intersects
281    * with the horizontal rectangle which opposite corners are at points
282    * (<code>x0</code>, <code>y0</code>) and (<code>x1</code>, <code>y1</code>).
283    */
intersectsRectangle(float x0, float y0, float x1, float y1)284   public boolean intersectsRectangle(float x0, float y0,
285                                      float x1, float y1) {
286     Rectangle2D rectangle = new Rectangle2D.Float(x0, y0, 0, 0);
287     rectangle.add(x1, y1);
288     return new Ellipse2D.Float(getX() - getDiameter() / 2, getY() - getDiameter() / 2, getDiameter(), getDiameter()).intersects(rectangle);
289   }
290 
291   /**
292    * Returns <code>true</code> if the disc of this compass contains
293    * the point at (<code>x</code>, <code>y</code>)
294    * with a given <code>margin</code>.
295    */
containsPoint(float x, float y, float margin)296   public boolean containsPoint(float x, float y, float margin) {
297     Ellipse2D shape = new Ellipse2D.Float(getX() - getDiameter() / 2, getY() - getDiameter() / 2, getDiameter(), getDiameter());
298     if (margin == 0) {
299       return shape.contains(x, y);
300     } else {
301       return shape.intersects(x - margin, y - margin, 2 * margin, 2 * margin);
302     }
303   }
304 
305   /**
306    * Moves this compass of (<code>dx</code>, <code>dy</code>) units.
307    */
move(float dx, float dy)308   public void move(float dx, float dy) {
309     setX(getX() + dx);
310     setY(getY() + dy);
311   }
312 
313   /**
314    * Returns a clone of this compass.
315    */
316   @Override
clone()317   public Compass clone() {
318     return (Compass)super.clone();
319   }
320 
321   /**
322    * Returns the elevation angle of the Sun in the sky in radians at a given
323    * <code>date</code> in milliseconds since the Epoch.
324    * See <a href="http://en.wikipedia.org/wiki/Horizontal_coordinate_system">Sun
325    * azimuth and elevation angles</a> for more information.
326    */
getSunElevation(long date)327   public float getSunElevation(long date) {
328     updateSunLocation(date);
329     return this.sunElevationCache;
330   }
331 
332   /**
333    * Returns the azimuth angle of the Sun in the sky in radians at a given
334    * <code>date</code> in milliseconds since the Epoch.
335    * See <a href="http://en.wikipedia.org/wiki/Horizontal_coordinate_system">Sun
336    * azimuth and elevation angles</a> for more information.
337    */
getSunAzimuth(long date)338   public float getSunAzimuth(long date) {
339     updateSunLocation(date);
340     return this.sunAzimuthCache;
341   }
342 
343   /**
344    * Computes the Sun's location in the sky at a given <code>date</code>.
345    */
updateSunLocation(long date)346   private void updateSunLocation(long date) {
347     if (this.dateCache == null
348         || this.dateCache.getTimeInMillis() != date) {
349       this.dateCache = new GregorianCalendar(this.timeZone);
350       this.dateCache.setTimeInMillis(date);
351 
352       int year = this.dateCache.get(Calendar.YEAR);
353       int month = this.dateCache.get(Calendar.MONTH) + 1; // Based on 1 for January
354       int day = this.dateCache.get(Calendar.DAY_OF_MONTH);
355       int hour = this.dateCache.get(Calendar.HOUR_OF_DAY);
356       int minute = this.dateCache.get(Calendar.MINUTE);
357       int second = this.dateCache.get(Calendar.SECOND);
358       int timeZone = this.dateCache.getTimeZone().getRawOffset() / 3600000;
359       int savingTime = this.dateCache.get(Calendar.DST_OFFSET) / 3600000;
360 
361       double julianDay = computeJulianDay(year, month, day, hour, minute, second, timeZone, savingTime);
362       double siderealTime = toSiderealTime(julianDay);
363       double angleH = 360. * siderealTime / 23.9344;
364       double angleT = (hour - (timeZone + savingTime) - 12. + minute / 60. + second / 3600.) * 360. / 23.9344;
365       double angle = angleH + angleT;
366 
367       // Compute equatorial coordinates
368       double g = 357.529 + 0.98560028 * julianDay;
369       double q = 280.459 + 0.98564736 * julianDay;
370       double l = q + 1.915 * Math.sin(Math.toRadians(g)) + 0.020 * Math.sin(Math.toRadians(2 * g));
371       double e = 23.439 - 0.00000036 * julianDay;
372       double rightAscension = Math.toDegrees(Math.atan(Math.cos(Math.toRadians(e)) * Math.sin(Math.toRadians(l)) / Math.cos(Math.toRadians(l)))) / 15.;
373       if (Math.cos(Math.toRadians(l)) < 0.) {
374         rightAscension += 12.;
375       }
376       if ((Math.cos(Math.toRadians(l)) > 0.) && (Math.sin(Math.toRadians(l)) < 0.)) {
377         rightAscension += 24.;
378       }
379       double declination = Math.asin(Math.sin(Math.toRadians(e)) * Math.sin(Math.toRadians(l)));
380 
381       double hourAngle = Math.toRadians(angle - rightAscension * 15. + Math.toDegrees(this.longitude));
382       double elevation = Math.asin(Math.sin(declination) * Math.sin(this.latitude) - Math.cos(declination) * Math.cos(this.latitude) * Math.cos(hourAngle));
383       double azimuth = Math.acos((Math.sin(declination) - Math.sin(this.latitude) * Math.sin(elevation)) / (Math.cos(this.latitude) * Math.cos(elevation)));
384       double sinAzimuth = (Math.cos(declination) * Math.sin(hourAngle)) / Math.cos(elevation);
385       if (sinAzimuth < 0.) {
386         azimuth = Math.PI * 2 - azimuth;
387       }
388 
389       this.sunElevationCache = (float)elevation;
390       this.sunAzimuthCache = (float)azimuth;
391     }
392   }
393 
computeJulianDay(int year, int month, int day, int hour, int minute, int second, int timeZone, int savingTime)394   private double computeJulianDay(int year, int month, int day, int hour, int minute, int second, int timeZone, int savingTime) {
395     double dayPart = day + hour / 24. + minute / 1440. + second / 86400.;
396     if (month == 1 || month == 2) {
397         year -= 1;
398         month += 12;
399     }
400     int a = year / 100;
401     int b = 2 - a + a / 4;
402 
403     double julianDay = (int)(365.25 * (year + 4716.)) + (int)((30.6001 * (month + 1))) + dayPart + b - 1524.5;
404     julianDay -= (timeZone + savingTime) / 24.;
405     julianDay -= 2451545.;
406     return julianDay;
407   }
408 
toSiderealTime(double julianDay)409   private double toSiderealTime(double julianDay) {
410     double centuries = julianDay / 36525.;
411     double siderealTime = (24110.54841 + (8640184.812866 * centuries) + (0.093104 * Math.pow(centuries, 2.)) - (0.0000062 * Math.pow(centuries, 3.))) / 3600.;
412     return ((siderealTime / 24.) - (int)(siderealTime / 24.)) * 24.;
413   }
414 
415   /**
416    * Inits the latitudeInDegrees and longitudeInDegrees where this compass is located from the id of the default time zone.
417    */
initGeographicPoint()418   private void initGeographicPoint() {
419     Map<String, GeographicPoint> timeZoneGeographicPoints;
420     if (timeZoneGeographicPointsReference != null) {
421       timeZoneGeographicPoints = timeZoneGeographicPointsReference.get();
422     } else {
423       timeZoneGeographicPoints = null;
424     }
425 
426     if (timeZoneGeographicPoints == null) {
427       timeZoneGeographicPoints = new HashMap<String, GeographicPoint>();
428       // Default geographic points for Java 6 time zone ids
429       // from free data provided by MaxMind WorldCities and Postal Code Databases
430       // at http://www.maxmind.com/app/worldcities and from Wikipedia
431       GeographicPoint apia = new GeographicPoint(-13.8333333f, -171.7333333f);
432       timeZoneGeographicPoints.put("Etc/GMT+11", apia); // Apia
433       timeZoneGeographicPoints.put("Pacific/Apia", apia);
434       timeZoneGeographicPoints.put("Pacific/Midway", new GeographicPoint(28.2f, -177.35f));
435       timeZoneGeographicPoints.put("Pacific/Niue", new GeographicPoint(-19.055f, -169.92f)); // Alofi
436       timeZoneGeographicPoints.put("Pacific/Pago_Pago", new GeographicPoint(-14.2780556f, -170.7025000f));
437       timeZoneGeographicPoints.put("Pacific/Samoa", apia);
438       timeZoneGeographicPoints.put("US/Samoa", apia);
439       timeZoneGeographicPoints.put("America/Adak", new GeographicPoint(51.8800000f, -176.6580556f));
440       timeZoneGeographicPoints.put("America/Atka", new GeographicPoint(52.1961111f, -174.2005556f));
441       GeographicPoint honolulu = new GeographicPoint(21.3069444f, -157.8583333f);
442       timeZoneGeographicPoints.put("Etc/GMT+10", honolulu); // Honolulu
443       timeZoneGeographicPoints.put("Pacific/Fakaofo", new GeographicPoint(-9.3653f, -171.215f));
444       timeZoneGeographicPoints.put("Pacific/Honolulu", honolulu);
445       timeZoneGeographicPoints.put("Pacific/Johnston", new GeographicPoint(16.75f, -169.517f));
446       timeZoneGeographicPoints.put("Pacific/Rarotonga", new GeographicPoint(-21.233f, -159.783f));
447       timeZoneGeographicPoints.put("Pacific/Tahiti", new GeographicPoint(-17.5333333f, -149.5666667f)); // Papeete
448       timeZoneGeographicPoints.put("SystemV/HST10", honolulu); // Honolulu
449       timeZoneGeographicPoints.put("US/Aleutian", new GeographicPoint(54.817f, 164.033f));
450       timeZoneGeographicPoints.put("US/Hawaii", honolulu); // Honolulu
451       timeZoneGeographicPoints.put("Pacific/Marquesas", new GeographicPoint(-9.45f, -139.38f));
452       GeographicPoint anchorage = new GeographicPoint(61.2180556f, -149.9002778f);
453       timeZoneGeographicPoints.put("America/Anchorage", anchorage);
454       timeZoneGeographicPoints.put("America/Juneau", new GeographicPoint(58.3019444f, -134.4197222f));
455       timeZoneGeographicPoints.put("America/Nome", new GeographicPoint(64.5011111f, -165.4063889f));
456       timeZoneGeographicPoints.put("America/Yakutat", new GeographicPoint(59.5469444f, -139.7272222f));
457       timeZoneGeographicPoints.put("Etc/GMT+9", anchorage); // Anchorage
458       timeZoneGeographicPoints.put("Pacific/Gambier", new GeographicPoint(-23.1178f, -134.97f));
459       timeZoneGeographicPoints.put("SystemV/YST9", anchorage); // Anchorage
460       timeZoneGeographicPoints.put("SystemV/YST9YDT", anchorage); // Anchorage
461       timeZoneGeographicPoints.put("US/Alaska", anchorage); // Anchorage
462       timeZoneGeographicPoints.put("America/Dawson", new GeographicPoint(64.066667f, -139.416667f));
463       timeZoneGeographicPoints.put("America/Ensenada", new GeographicPoint(31.866667f, -116.616667f));
464       GeographicPoint losAngeles = new GeographicPoint(34.0522222f, -118.2427778f);
465       timeZoneGeographicPoints.put("America/Los_Angeles", losAngeles);
466       timeZoneGeographicPoints.put("America/Santa_Isabel", new GeographicPoint(28.383333f, -113.35f));
467       timeZoneGeographicPoints.put("America/Tijuana", new GeographicPoint(32.533333f, -117.016667f));
468       timeZoneGeographicPoints.put("America/Vancouver", new GeographicPoint(49.25f, -123.133333f));
469       timeZoneGeographicPoints.put("America/Whitehorse", new GeographicPoint(60.716667f, -135.05f));
470       timeZoneGeographicPoints.put("Canada/Pacific", new GeographicPoint(49.25f, -123.133333f)); // Vancouver
471       timeZoneGeographicPoints.put("Canada/Yukon", new GeographicPoint(60.716667f, -135.05f)); // Whitehorse
472       timeZoneGeographicPoints.put("Etc/GMT+8", losAngeles); // Los Angeles
473       timeZoneGeographicPoints.put("Mexico/BajaNorte", new GeographicPoint(32.533333f, -117.016667f)); // Tijuana
474       timeZoneGeographicPoints.put("Pacific/Pitcairn", new GeographicPoint(-25.0667f, -130.1f)); // Adamstown
475       timeZoneGeographicPoints.put("SystemV/PST8", losAngeles); // Los Angeles
476       timeZoneGeographicPoints.put("SystemV/PST8PDT", losAngeles); // Los Angeles
477       timeZoneGeographicPoints.put("US/Pacific", losAngeles); // Los Angeles
478       timeZoneGeographicPoints.put("US/Pacific-New", losAngeles); // Los Angeles
479       timeZoneGeographicPoints.put("America/Boise", new GeographicPoint(43.6136111f, -116.2025000f));
480       timeZoneGeographicPoints.put("America/Cambridge_Bay", new GeographicPoint(69.116667f, -105.033333f));
481       timeZoneGeographicPoints.put("America/Chihuahua", new GeographicPoint(28.633333f, -106.083333f));
482       timeZoneGeographicPoints.put("America/Dawson_Creek", new GeographicPoint(55.766667f, -120.233333f));
483       GeographicPoint denver = new GeographicPoint(39.7391667f, -104.9841667f);
484       timeZoneGeographicPoints.put("America/Denver", denver);
485       timeZoneGeographicPoints.put("America/Edmonton", new GeographicPoint(53.55f, -113.5f));
486       timeZoneGeographicPoints.put("America/Hermosillo", new GeographicPoint(29.066667f, -110.966667f));
487       timeZoneGeographicPoints.put("America/Inuvik", new GeographicPoint(68.35f, -133.7f));
488       timeZoneGeographicPoints.put("America/Mazatlan", new GeographicPoint(23.216667f, -106.416667f));
489       timeZoneGeographicPoints.put("America/Ojinaga", new GeographicPoint(29.566667f, -104.416667f));
490       timeZoneGeographicPoints.put("America/Phoenix", new GeographicPoint(33.4483333f, -112.0733333f));
491       timeZoneGeographicPoints.put("America/Shiprock", new GeographicPoint(36.7855556f, -108.6863889f));
492       timeZoneGeographicPoints.put("America/Yellowknife", new GeographicPoint(62.45f, -114.35f));
493       timeZoneGeographicPoints.put("Canada/Mountain", new GeographicPoint(53.55f, -113.5f)); // Edmonton
494       timeZoneGeographicPoints.put("Etc/GMT+7", denver); // Denver
495       timeZoneGeographicPoints.put("Mexico/BajaSur", new GeographicPoint(32.567f, -116.633f)); // Tecate
496       timeZoneGeographicPoints.put("SystemV/MST7", denver); // Denver
497       timeZoneGeographicPoints.put("SystemV/MST7MDT", denver); // Denver
498       timeZoneGeographicPoints.put("US/Arizona", new GeographicPoint(33.4483333f, -112.0733333f)); // Phoenix
499       timeZoneGeographicPoints.put("US/Mountain", denver); // Denver
500       timeZoneGeographicPoints.put("America/Belize", new GeographicPoint(17.4833333f, -88.1833333f));
501       timeZoneGeographicPoints.put("America/Cancun", new GeographicPoint(21.166667f, -86.833333f));
502       GeographicPoint chicago = new GeographicPoint(41.8500000f, -87.6500000f);
503       timeZoneGeographicPoints.put("America/Chicago", chicago);
504       timeZoneGeographicPoints.put("America/Costa_Rica", new GeographicPoint(9.9333333f, -84.0833333f)); // San Jose
505       timeZoneGeographicPoints.put("America/El_Salvador", new GeographicPoint(13.7086111f, -89.2030556f)); // San Salvador
506       timeZoneGeographicPoints.put("America/Guatemala", new GeographicPoint(14.6211111f, -90.5269444f));
507       timeZoneGeographicPoints.put("America/Knox_IN", new GeographicPoint(41.2958333f, -86.6250000f)); // Knox
508       timeZoneGeographicPoints.put("America/Managua", new GeographicPoint(12.1508333f, -86.2683333f));
509       timeZoneGeographicPoints.put("America/Matamoros", new GeographicPoint(25.883333f, -97.5f));
510       timeZoneGeographicPoints.put("America/Menominee", new GeographicPoint(45.1077778f, -87.6141667f));
511       timeZoneGeographicPoints.put("America/Merida", new GeographicPoint(20.966667f, -89.616667f));
512       timeZoneGeographicPoints.put("America/Mexico_City", new GeographicPoint(19.434167f, -99.138611f));
513       timeZoneGeographicPoints.put("America/Monterrey", new GeographicPoint(25.666667f, -100.316667f));
514       timeZoneGeographicPoints.put("America/Rainy_River", new GeographicPoint(48.716667f, -94.566667f));
515       timeZoneGeographicPoints.put("America/Rankin_Inlet", new GeographicPoint(62.816667f, -92.083333f));
516       timeZoneGeographicPoints.put("America/Regina", new GeographicPoint(50.45f, -104.616667f));
517       timeZoneGeographicPoints.put("America/Swift_Current", new GeographicPoint(50.283333f, -107.766667f));
518       timeZoneGeographicPoints.put("America/Tegucigalpa", new GeographicPoint(14.1f, -87.2166667f));
519       timeZoneGeographicPoints.put("America/Winnipeg", new GeographicPoint(49.883333f, -97.166667f));
520       timeZoneGeographicPoints.put("Canada/Central", new GeographicPoint(50.45f, -104.616667f)); // Regina
521       timeZoneGeographicPoints.put("Canada/East-Saskatchewan", new GeographicPoint(51.216667f, -102.466667f)); // Yorkton
522       timeZoneGeographicPoints.put("Canada/Saskatchewan", new GeographicPoint(50.45f, -104.616667f)); // Regina
523       timeZoneGeographicPoints.put("Chile/EasterIsland", new GeographicPoint(-27.15f, -109.425f));
524       timeZoneGeographicPoints.put("Etc/GMT+6", chicago); // Chicago
525       timeZoneGeographicPoints.put("Mexico/General", new GeographicPoint(19.434167f, -99.138611f)); // Mexico City
526       timeZoneGeographicPoints.put("Pacific/Easter", new GeographicPoint(-27.15f, -109.425f)); // Easter Island
527       timeZoneGeographicPoints.put("Pacific/Galapagos", new GeographicPoint(-0.667f, -90.55f));
528       timeZoneGeographicPoints.put("SystemV/CST6", chicago); // Chicago
529       timeZoneGeographicPoints.put("SystemV/CST6CDT", chicago); // Chicago
530       timeZoneGeographicPoints.put("US/Central", chicago); // Chicago
531       timeZoneGeographicPoints.put("US/Indiana-Starke", new GeographicPoint(41.2958333f, -86.6250000f)); // Knox
532       timeZoneGeographicPoints.put("America/Atikokan", new GeographicPoint(48.75f, -91.616667f));
533       timeZoneGeographicPoints.put("America/Bogota", new GeographicPoint(4.6f, -74.0833333f));
534       timeZoneGeographicPoints.put("America/Cayman", new GeographicPoint(19.3f, -81.3833333f)); // George Town
535       timeZoneGeographicPoints.put("America/Coral_Harbour", new GeographicPoint(64.133333f, -83.166667f));
536       timeZoneGeographicPoints.put("America/Detroit", new GeographicPoint(42.3313889f, -83.0458333f));
537       timeZoneGeographicPoints.put("America/Fort_Wayne", new GeographicPoint(41.1305556f, -85.1288889f));
538       timeZoneGeographicPoints.put("America/Grand_Turk", new GeographicPoint(21.4666667f, -71.1333333f));
539       timeZoneGeographicPoints.put("America/Guayaquil", new GeographicPoint(-2.1666667f, -79.9f));
540       timeZoneGeographicPoints.put("America/Havana", new GeographicPoint(23.1319444f, -82.3641667f));
541       timeZoneGeographicPoints.put("America/Indianapolis", new GeographicPoint(39.7683333f, -86.1580556f));
542       timeZoneGeographicPoints.put("America/Iqaluit", new GeographicPoint(63.733333f, -68.5f));
543       timeZoneGeographicPoints.put("America/Jamaica", new GeographicPoint(18.0f, -76.8f)); // Kingston
544       timeZoneGeographicPoints.put("America/Lima", new GeographicPoint(-12.05f, -77.05f));
545       timeZoneGeographicPoints.put("America/Louisville", new GeographicPoint(38.2541667f, -85.7594444f));
546       timeZoneGeographicPoints.put("America/Montreal", new GeographicPoint(45.5f, -73.583333f));
547       timeZoneGeographicPoints.put("America/Nassau", new GeographicPoint(25.0833333f, -77.35f));
548       GeographicPoint newYork = new GeographicPoint(40.7141667f, -74.0063889f);
549       timeZoneGeographicPoints.put("America/New_York", newYork);
550       timeZoneGeographicPoints.put("America/Nipigon", new GeographicPoint(49.016667f, -88.25f));
551       timeZoneGeographicPoints.put("America/Panama", new GeographicPoint(8.9666667f, -79.5333333f));
552       timeZoneGeographicPoints.put("America/Pangnirtung", new GeographicPoint(66.133333f, -65.75f));
553       timeZoneGeographicPoints.put("America/Port-au-Prince", new GeographicPoint(18.5391667f, -72.335f));
554       timeZoneGeographicPoints.put("America/Resolute", new GeographicPoint(74.683333f, -94.9f));
555       timeZoneGeographicPoints.put("America/Thunder_Bay", new GeographicPoint(48.4f, -89.233333f));
556       timeZoneGeographicPoints.put("America/Toronto", new GeographicPoint(43.666667f, -79.416667f));
557       timeZoneGeographicPoints.put("Canada/Eastern", new GeographicPoint(43.666667f, -79.416667f)); // Toronto
558       timeZoneGeographicPoints.put("Etc/GMT+5", newYork); // New York
559       timeZoneGeographicPoints.put("SystemV/EST5", newYork); // New York
560       timeZoneGeographicPoints.put("SystemV/EST5EDT", newYork); // New York
561       timeZoneGeographicPoints.put("US/East-Indiana", new GeographicPoint(36.8381f, -84.85f)); // Monticello
562       timeZoneGeographicPoints.put("US/Eastern", newYork); // New York
563       timeZoneGeographicPoints.put("US/Michigan", new GeographicPoint(42.3313889f, -83.0458333f)); // Detroit
564       timeZoneGeographicPoints.put("America/Caracas", new GeographicPoint(10.5f, -66.9166667f));
565       timeZoneGeographicPoints.put("America/Anguilla", new GeographicPoint(18.2166667f, -63.05f)); // The Valley
566       timeZoneGeographicPoints.put("America/Antigua", new GeographicPoint(17.1166667f, -61.85f)); // Saint John's
567       timeZoneGeographicPoints.put("America/Aruba", new GeographicPoint(10.5411111f, -72.9175f));
568       timeZoneGeographicPoints.put("America/Asuncion", new GeographicPoint(-25.2666667f, -57.6666667f));
569       timeZoneGeographicPoints.put("America/Barbados", new GeographicPoint(13.1f, -59.6166667f)); // Bridgetown
570       timeZoneGeographicPoints.put("America/Blanc-Sablon", new GeographicPoint(51.433333f, -57.116667f));
571       timeZoneGeographicPoints.put("America/Boa_Vista", new GeographicPoint(2.8166667f, -60.6666667f));
572       timeZoneGeographicPoints.put("America/Campo_Grande", new GeographicPoint(-20.45f, -54.6166667f));
573       timeZoneGeographicPoints.put("America/Cuiaba", new GeographicPoint(-15.5833333f, -56.0833333f));
574       timeZoneGeographicPoints.put("America/Curacao", new GeographicPoint(12.1167f, -68.933f)); // Willemstad
575       timeZoneGeographicPoints.put("America/Dominica", new GeographicPoint(15.3f, -61.4f)); // Roseau
576       timeZoneGeographicPoints.put("America/Eirunepe", new GeographicPoint(-6.6666667f, -69.8666667f));
577       timeZoneGeographicPoints.put("America/Glace_Bay", new GeographicPoint(46.2f, -59.966667f));
578       timeZoneGeographicPoints.put("America/Goose_Bay", new GeographicPoint(53.333333f, -60.416667f));
579       timeZoneGeographicPoints.put("America/Grenada", new GeographicPoint(12.05f, -61.75f)); // Saint George
580       timeZoneGeographicPoints.put("America/Guadeloupe", new GeographicPoint(16.2333333f, -61.5166667f)); // Pointe-a-Pitre
581       timeZoneGeographicPoints.put("America/Guyana", new GeographicPoint(6.8f, -58.1666667f)); // Georgetown
582       timeZoneGeographicPoints.put("America/Halifax", new GeographicPoint(44.65f, -63.6f));
583       timeZoneGeographicPoints.put("America/La_Paz", new GeographicPoint(-16.5f, -68.15f));
584       timeZoneGeographicPoints.put("America/Manaus", new GeographicPoint(-3.1133333f, -60.0252778f));
585       timeZoneGeographicPoints.put("America/Marigot", new GeographicPoint(18.073f, 63.0844f)); // St Martin Island
586       timeZoneGeographicPoints.put("America/Martinique", new GeographicPoint(14.6f, -61.0833333f)); // Fort-de-France
587       timeZoneGeographicPoints.put("America/Moncton", new GeographicPoint(46.083333f, -64.766667f));
588       timeZoneGeographicPoints.put("America/Montserrat", new GeographicPoint(16.7f, -62.2166667f)); // Plymouth
589       timeZoneGeographicPoints.put("America/Port_of_Spain", new GeographicPoint(10.65f, -61.5166667f));
590       timeZoneGeographicPoints.put("America/Porto_Acre", new GeographicPoint(-9.5877778f, -67.5355556f));
591       timeZoneGeographicPoints.put("America/Porto_Velho", new GeographicPoint(-8.7666667f, -63.9f));
592       timeZoneGeographicPoints.put("America/Puerto_Rico", new GeographicPoint(18.467f, 66.117f)); // San Juan
593       timeZoneGeographicPoints.put("America/Rio_Branco", new GeographicPoint(-9.9666667f, -67.8f));
594       GeographicPoint santiago = new GeographicPoint(-33.45f, -70.6666667f);
595       timeZoneGeographicPoints.put("America/Santiago", santiago);
596       timeZoneGeographicPoints.put("America/Santo_Domingo", new GeographicPoint(18.4666667f, -69.9f));
597       timeZoneGeographicPoints.put("America/St_Barthelemy", new GeographicPoint(17.8978f, -62.851f)); // Gustavia
598       timeZoneGeographicPoints.put("America/St_Kitts", new GeographicPoint(17.3f, -62.733f)); // Basseterre
599       timeZoneGeographicPoints.put("America/St_Lucia", new GeographicPoint(14.0167f, -60.9833f)); // Castries
600       timeZoneGeographicPoints.put("America/St_Thomas", new GeographicPoint(18.3333f, -64.9167f));
601       timeZoneGeographicPoints.put("America/St_Vincent", new GeographicPoint(13.1667f, -61.2333f)); // Kingstown
602       timeZoneGeographicPoints.put("America/Thule", new GeographicPoint(-54.2766667f, -36.5116667f)); // Grytviken
603       timeZoneGeographicPoints.put("America/Tortola", new GeographicPoint(18.4166667f, -64.6166667f)); // Road Town
604       timeZoneGeographicPoints.put("America/Virgin", new GeographicPoint(18.3438889f, -64.9311111f)); // Charlotte Amalie
605       timeZoneGeographicPoints.put("Antarctica/Palmer", new GeographicPoint(-64.25f, -62.833f));
606       timeZoneGeographicPoints.put("Atlantic/Bermuda", new GeographicPoint(32.2941667f, -64.7838889f)); // Hamilton
607       timeZoneGeographicPoints.put("Atlantic/Stanley", new GeographicPoint(-51.7f, -57.85f));
608       timeZoneGeographicPoints.put("Brazil/Acre", new GeographicPoint(-10.8833333f, -45.0833333f));
609       timeZoneGeographicPoints.put("Brazil/West", new GeographicPoint(-10.8833333f, -45.0833333f)); // Acre
610       timeZoneGeographicPoints.put("Canada/Atlantic", new GeographicPoint(44.65f, -63.6f)); // Halifax
611       timeZoneGeographicPoints.put("Chile/Continental", santiago); // Santiago
612       timeZoneGeographicPoints.put("Etc/GMT+4", santiago); // Santiago
613       timeZoneGeographicPoints.put("SystemV/AST4", new GeographicPoint(44.65f, -63.6f)); // Halifax
614       timeZoneGeographicPoints.put("SystemV/AST4ADT", new GeographicPoint(44.65f, -63.6f)); // Halifax
615       timeZoneGeographicPoints.put("America/St_Johns", new GeographicPoint(47.5675f, -52.7072f));
616       timeZoneGeographicPoints.put("Canada/Newfoundland", new GeographicPoint(47.5675f, -52.7072f)); // St John's
617       timeZoneGeographicPoints.put("America/Araguaina", new GeographicPoint(-7.16f, -48.0575f));
618       timeZoneGeographicPoints.put("America/Bahia", new GeographicPoint(-12.9833333f, -38.5166667f)); // Salvador
619       timeZoneGeographicPoints.put("America/Belem", new GeographicPoint(-1.45f, -48.4833333f));
620       timeZoneGeographicPoints.put("America/Buenos_Aires", new GeographicPoint(-34.5875f, -58.6725f));
621       timeZoneGeographicPoints.put("America/Catamarca", new GeographicPoint(-28.4666667f, -65.7833333f));
622       timeZoneGeographicPoints.put("America/Cayenne", new GeographicPoint(4.9333333f, -52.3333333f));
623       timeZoneGeographicPoints.put("America/Cordoba", new GeographicPoint(-31.4f, -64.1833333f));
624       timeZoneGeographicPoints.put("America/Fortaleza", new GeographicPoint(-3.7166667f, -38.5f));
625       timeZoneGeographicPoints.put("America/Godthab", new GeographicPoint(64.1833333f, -51.75f));
626       timeZoneGeographicPoints.put("America/Jujuy", new GeographicPoint(-24.1833333f, -65.3f));
627       timeZoneGeographicPoints.put("America/Maceio", new GeographicPoint(-9.6666667f, -35.7166667f));
628       timeZoneGeographicPoints.put("America/Mendoza", new GeographicPoint(-32.8833333f, -68.8166667f));
629       timeZoneGeographicPoints.put("America/Miquelon", new GeographicPoint(47.0975f, -56.3813889f));
630       timeZoneGeographicPoints.put("America/Montevideo", new GeographicPoint(-34.8580556f, -56.1708333f));
631       timeZoneGeographicPoints.put("America/Paramaribo", new GeographicPoint(5.8333333f, -55.1666667f));
632       timeZoneGeographicPoints.put("America/Recife", new GeographicPoint(-8.05f, -34.9f));
633       timeZoneGeographicPoints.put("America/Rosario", new GeographicPoint(-32.9511111f, -60.6663889f));
634       timeZoneGeographicPoints.put("America/Santarem", new GeographicPoint(-2.4333333f, -54.7f));
635       GeographicPoint saoPaulo = new GeographicPoint(-23.5333333f, -46.6166667f);
636       timeZoneGeographicPoints.put("America/Sao_Paulo", saoPaulo);
637       timeZoneGeographicPoints.put("Antarctica/Rothera", new GeographicPoint(67.567f, 68.133f));
638       timeZoneGeographicPoints.put("Brazil/East", saoPaulo); // Sao Paulo
639       timeZoneGeographicPoints.put("Etc/GMT+3", saoPaulo); // Sao Paulo
640       timeZoneGeographicPoints.put("America/Noronha", new GeographicPoint(3.85f, 25.417f));
641       GeographicPoint southGeorgia = new GeographicPoint(54.25f, 36.75f);
642       timeZoneGeographicPoints.put("Atlantic/South_Georgia", southGeorgia);
643       timeZoneGeographicPoints.put("Brazil/DeNoronha", new GeographicPoint(3.85f, 25.417f));
644       timeZoneGeographicPoints.put("Etc/GMT+2", southGeorgia); // South Georgia
645       timeZoneGeographicPoints.put("America/Scoresbysund", new GeographicPoint(70.4833333f, -21.9666667f));
646       GeographicPoint azores = new GeographicPoint(37.4833333f, -2.5666667f);
647       timeZoneGeographicPoints.put("Atlantic/Azores", azores);
648       timeZoneGeographicPoints.put("Atlantic/Cape_Verde", new GeographicPoint(14.9166667f, -23.5166667f)); // Praia
649       timeZoneGeographicPoints.put("Etc/GMT+1", azores); // Azores
650       timeZoneGeographicPoints.put("Africa/Abidjan", new GeographicPoint(5.341111f, -4.028056f));
651       timeZoneGeographicPoints.put("Africa/Accra", new GeographicPoint(5.55f, -0.2166667f));
652       timeZoneGeographicPoints.put("Africa/Bamako", new GeographicPoint(12.65f, -8.0f));
653       timeZoneGeographicPoints.put("Africa/Banjul", new GeographicPoint(13.4530556f, -16.5775f));
654       timeZoneGeographicPoints.put("Africa/Bissau", new GeographicPoint(11.85f, -15.5833333f));
655       timeZoneGeographicPoints.put("Africa/Casablanca", new GeographicPoint(33.5930556f, -7.6163889f));
656       timeZoneGeographicPoints.put("Africa/Conakry", new GeographicPoint(9.5091667f, -13.7122222f));
657       timeZoneGeographicPoints.put("Africa/Dakar", new GeographicPoint(14.6708333f, -17.4380556f));
658       timeZoneGeographicPoints.put("Africa/El_Aaiun", new GeographicPoint(27.1536111f, -13.2033333f));
659       timeZoneGeographicPoints.put("Africa/Freetown", new GeographicPoint(8.49f, -13.2341667f));
660       timeZoneGeographicPoints.put("Africa/Lome", new GeographicPoint(6.1319444f, 1.2227778f));
661       timeZoneGeographicPoints.put("Africa/Monrovia", new GeographicPoint(6.3105556f, -10.8047222f));
662       timeZoneGeographicPoints.put("Africa/Nouakchott", new GeographicPoint(18.0863889f, -15.9752778f));
663       timeZoneGeographicPoints.put("Africa/Ouagadougou", new GeographicPoint(12.3702778f, -1.5247222f));
664       timeZoneGeographicPoints.put("Africa/Sao_Tome", new GeographicPoint(0.3333333f, 6.7333333f));
665       timeZoneGeographicPoints.put("Africa/Timbuktu", new GeographicPoint(16.7666667f, -3.0166667f));
666       timeZoneGeographicPoints.put("America/Danmarkshavn", new GeographicPoint(76.767f, 18.667f));
667       timeZoneGeographicPoints.put("Atlantic/Canary", new GeographicPoint(28.45f, -16.2333333f)); // Santa Cruz de Tenerife
668       timeZoneGeographicPoints.put("Atlantic/Faeroe", new GeographicPoint(62.0166667f, -6.7666667f)); // Thorshavn
669       timeZoneGeographicPoints.put("Atlantic/Faroe", new GeographicPoint(62.0166667f, -6.7666667f)); // Thorshavn
670       timeZoneGeographicPoints.put("Atlantic/Madeira", new GeographicPoint(32.6333333f, -16.9f)); // Funchal
671       timeZoneGeographicPoints.put("Atlantic/Reykjavik", new GeographicPoint(64.15f, -21.95f));
672       timeZoneGeographicPoints.put("Atlantic/St_Helena", new GeographicPoint(-15.9333333f, -5.7166667f)); // Jamestown
673       GeographicPoint greenwich = new GeographicPoint(51.466667f, 0f);
674       timeZoneGeographicPoints.put("Etc/GMT", greenwich); // Greenwich
675       timeZoneGeographicPoints.put("Etc/GMT+0", greenwich); // Greenwich
676       timeZoneGeographicPoints.put("Etc/GMT-0", greenwich); // Greenwich
677       timeZoneGeographicPoints.put("Etc/GMT0", greenwich); // Greenwich
678       timeZoneGeographicPoints.put("Etc/Greenwich", greenwich);
679       timeZoneGeographicPoints.put("Etc/UCT", greenwich); // Greenwich
680       timeZoneGeographicPoints.put("Etc/UTC", greenwich); // Greenwich
681       timeZoneGeographicPoints.put("Etc/Universal", greenwich); // Greenwich
682       timeZoneGeographicPoints.put("Etc/Zulu", greenwich); // Greenwich
683       timeZoneGeographicPoints.put("Europe/Belfast", new GeographicPoint(54.583333f, -5.933333f));
684       timeZoneGeographicPoints.put("Europe/Dublin", new GeographicPoint(53.3330556f, -6.2488889f));
685       timeZoneGeographicPoints.put("Europe/Guernsey", new GeographicPoint(49.45f, -2.533f)); // Saint-Pierre-Port
686       timeZoneGeographicPoints.put("Europe/Isle_of_Man", new GeographicPoint(54.14521f, -4.48172f)); // Douglas
687       timeZoneGeographicPoints.put("Europe/Jersey", new GeographicPoint(49.2f, -2.117f)); // Saint-Helier
688       timeZoneGeographicPoints.put("Europe/Lisbon", new GeographicPoint(38.7166667f, -9.1333333f));
689       timeZoneGeographicPoints.put("Europe/London", new GeographicPoint(51.5f, -.116667f));
690       timeZoneGeographicPoints.put("Africa/Algiers", new GeographicPoint(36.7630556f, 3.0505556f));
691       timeZoneGeographicPoints.put("Africa/Bangui", new GeographicPoint(4.3666667f, 18.5833333f));
692       timeZoneGeographicPoints.put("Africa/Brazzaville", new GeographicPoint(-4.2591667f, 15.2847222f));
693       timeZoneGeographicPoints.put("Africa/Ceuta", new GeographicPoint(35.8902778f, -5.3075f));
694       timeZoneGeographicPoints.put("Africa/Douala", new GeographicPoint(4.0502778f, 9.7f));
695       timeZoneGeographicPoints.put("Africa/Kinshasa", new GeographicPoint(-4.3f, 15.3f));
696       timeZoneGeographicPoints.put("Africa/Lagos", new GeographicPoint(6.4530556f, 3.3958333f));
697       timeZoneGeographicPoints.put("Africa/Libreville", new GeographicPoint(0.3833333f, 9.45f));
698       timeZoneGeographicPoints.put("Africa/Luanda", new GeographicPoint(-8.8383333f, 13.2344444f));
699       timeZoneGeographicPoints.put("Africa/Malabo", new GeographicPoint(3.75f, 8.7833333f));
700       timeZoneGeographicPoints.put("Africa/Ndjamena", new GeographicPoint(12.1130556f, 15.0491667f));
701       timeZoneGeographicPoints.put("Africa/Niamey", new GeographicPoint(13.5166667f, 2.1166667f));
702       timeZoneGeographicPoints.put("Africa/Porto-Novo", new GeographicPoint(6.4833333f, 2.6166667f));
703       timeZoneGeographicPoints.put("Africa/Tunis", new GeographicPoint(36.8027778f, 10.1797222f));
704       timeZoneGeographicPoints.put("Africa/Windhoek", new GeographicPoint(-22.57f, 17.0836111f));
705       timeZoneGeographicPoints.put("Arctic/Longyearbyen", new GeographicPoint(78.2166667f, 15.6333333f));
706       timeZoneGeographicPoints.put("Atlantic/Jan_Mayen", new GeographicPoint(71f, -8.333f));
707       GeographicPoint paris = new GeographicPoint(48.866667f, 2.333333f);
708       timeZoneGeographicPoints.put("Etc/GMT-1", paris); // Paris
709       timeZoneGeographicPoints.put("Europe/Amsterdam", new GeographicPoint(52.35f, 4.9166667f));
710       timeZoneGeographicPoints.put("Europe/Andorra", new GeographicPoint(42.5f, 1.5166667f));
711       timeZoneGeographicPoints.put("Europe/Belgrade", new GeographicPoint(44.818611f, 20.468056f));
712       timeZoneGeographicPoints.put("Europe/Berlin", new GeographicPoint(52.5166667f, 13.4f));
713       timeZoneGeographicPoints.put("Europe/Bratislava", new GeographicPoint(48.15f, 17.1166667f));
714       timeZoneGeographicPoints.put("Europe/Brussels", new GeographicPoint(50.833333f, 4.333333f));
715       timeZoneGeographicPoints.put("Europe/Budapest", new GeographicPoint(47.5f, 19.0833333f));
716       timeZoneGeographicPoints.put("Europe/Copenhagen", new GeographicPoint(55.6666667f, 12.5833333f));
717       timeZoneGeographicPoints.put("Europe/Gibraltar", new GeographicPoint(36.1333333f, -5.35f));
718       timeZoneGeographicPoints.put("Europe/Ljubljana", new GeographicPoint(46.0552778f, 14.5144444f));
719       timeZoneGeographicPoints.put("Europe/Luxembourg", new GeographicPoint(49.6116667f, 6.13f));
720       timeZoneGeographicPoints.put("Europe/Madrid", new GeographicPoint(40.4f, -3.6833333f));
721       timeZoneGeographicPoints.put("Europe/Malta", new GeographicPoint(35.8997222f, 14.5147222f)); // La Valette
722       timeZoneGeographicPoints.put("Europe/Monaco", new GeographicPoint(43.7333333f, 7.4166667f));
723       timeZoneGeographicPoints.put("Europe/Oslo", new GeographicPoint(59.916667f, 10.75f));
724       timeZoneGeographicPoints.put("Europe/Paris", paris);
725       timeZoneGeographicPoints.put("Europe/Podgorica", new GeographicPoint(42.441111f, 19.263611f));
726       timeZoneGeographicPoints.put("Europe/Prague", new GeographicPoint(50.0833333f, 14.4666667f));
727       timeZoneGeographicPoints.put("Europe/Rome", new GeographicPoint(41.9f, 12.4833333f));
728       timeZoneGeographicPoints.put("Europe/San_Marino", new GeographicPoint(43.9333333f, 12.45f));
729       timeZoneGeographicPoints.put("Europe/Sarajevo", new GeographicPoint(43.85f, 18.3833333f));
730       timeZoneGeographicPoints.put("Europe/Skopje", new GeographicPoint(42.0f, 21.4333333f));
731       timeZoneGeographicPoints.put("Europe/Stockholm", new GeographicPoint(59.3333333f, 18.05f));
732       timeZoneGeographicPoints.put("Europe/Tirane", new GeographicPoint(41.3275f, 19.8188889f));
733       timeZoneGeographicPoints.put("Europe/Vaduz", new GeographicPoint(47.1333333f, 9.5166667f));
734       timeZoneGeographicPoints.put("Europe/Vatican", new GeographicPoint(41.9f, 12.45f));
735       timeZoneGeographicPoints.put("Europe/Vienna", new GeographicPoint(48.2f, 16.3666667f));
736       timeZoneGeographicPoints.put("Europe/Warsaw", new GeographicPoint(52.25f, 21.0f));
737       timeZoneGeographicPoints.put("Europe/Zagreb", new GeographicPoint(45.8f, 16.0f));
738       timeZoneGeographicPoints.put("Europe/Zurich", new GeographicPoint(47.3666667f, 8.55f));
739       timeZoneGeographicPoints.put("Africa/Blantyre", new GeographicPoint(-15.7833333f, 35.0f));
740       timeZoneGeographicPoints.put("Africa/Bujumbura", new GeographicPoint(-3.3761111f, 29.36f));
741       timeZoneGeographicPoints.put("Africa/Cairo", new GeographicPoint(30.05f, 31.25f));
742       timeZoneGeographicPoints.put("Africa/Gaborone", new GeographicPoint(-24.6463889f, 25.9119444f));
743       timeZoneGeographicPoints.put("Africa/Harare", new GeographicPoint(-17.8177778f, 31.0447222f));
744       timeZoneGeographicPoints.put("Africa/Johannesburg", new GeographicPoint(-26.2f, 28.0833333f));
745       timeZoneGeographicPoints.put("Africa/Kigali", new GeographicPoint(-1.9536111f, 30.0605556f));
746       timeZoneGeographicPoints.put("Africa/Lubumbashi", new GeographicPoint(-11.666667f, 27.466667f));
747       timeZoneGeographicPoints.put("Africa/Lusaka", new GeographicPoint(-15.4166667f, 28.2833333f));
748       timeZoneGeographicPoints.put("Africa/Maputo", new GeographicPoint(-25.9652778f, 32.5891667f));
749       timeZoneGeographicPoints.put("Africa/Maseru", new GeographicPoint(-29.3166667f, 27.4833333f));
750       timeZoneGeographicPoints.put("Africa/Mbabane", new GeographicPoint(-26.3166667f, 31.1333333f));
751       timeZoneGeographicPoints.put("Africa/Tripoli", new GeographicPoint(32.8925f, 13.18f));
752       timeZoneGeographicPoints.put("Asia/Amman", new GeographicPoint(31.95f, 35.9333333f));
753       timeZoneGeographicPoints.put("Asia/Beirut", new GeographicPoint(33.8719444f, 35.5097222f));
754       timeZoneGeographicPoints.put("Asia/Damascus", new GeographicPoint(33.5f, 36.3f));
755       timeZoneGeographicPoints.put("Asia/Gaza", new GeographicPoint(31.5f, 34.466667f));
756       timeZoneGeographicPoints.put("Asia/Istanbul", new GeographicPoint(41.0186111f, 28.9647222f));
757       timeZoneGeographicPoints.put("Asia/Jerusalem", new GeographicPoint(31.78f, 35.23f));
758       timeZoneGeographicPoints.put("Asia/Nicosia", new GeographicPoint(35.1666667f, 33.3666667f));
759       timeZoneGeographicPoints.put("Asia/Tel_Aviv", new GeographicPoint(32.0666667f, 34.7666667f));
760       GeographicPoint athens = new GeographicPoint(37.9833333f, 23.7333333f);
761       timeZoneGeographicPoints.put("Etc/GMT-2", athens); // Athens
762       timeZoneGeographicPoints.put("Europe/Athens", new GeographicPoint(37.9833333f, 23.7333333f));
763       timeZoneGeographicPoints.put("Europe/Bucharest", new GeographicPoint(44.4333333f, 26.1f));
764       timeZoneGeographicPoints.put("Europe/Chisinau", new GeographicPoint(47.0055556f, 28.8575f));
765       timeZoneGeographicPoints.put("Europe/Helsinki", new GeographicPoint(60.1755556f, 24.9341667f));
766       timeZoneGeographicPoints.put("Europe/Istanbul", new GeographicPoint(41.0186111f, 28.9647222f));
767       timeZoneGeographicPoints.put("Europe/Kaliningrad", new GeographicPoint(54.71f, 20.5f));
768       timeZoneGeographicPoints.put("Europe/Kiev", new GeographicPoint(50.4333333f, 30.5166667f));
769       timeZoneGeographicPoints.put("Europe/Mariehamn", new GeographicPoint(60.1f, 19.95f));
770       timeZoneGeographicPoints.put("Europe/Minsk", new GeographicPoint(53.9f, 27.5666667f));
771       timeZoneGeographicPoints.put("Europe/Nicosia", new GeographicPoint(35.1666667f, 33.3666667f));
772       timeZoneGeographicPoints.put("Europe/Riga", new GeographicPoint(56.95f, 24.1f));
773       timeZoneGeographicPoints.put("Europe/Simferopol", new GeographicPoint(44.95f, 34.1f));
774       timeZoneGeographicPoints.put("Europe/Sofia", new GeographicPoint(42.6833333f, 23.3166667f));
775       timeZoneGeographicPoints.put("Europe/Tallinn", new GeographicPoint(59.4338889f, 24.7280556f));
776       timeZoneGeographicPoints.put("Europe/Tiraspol", new GeographicPoint(46.8402778f, 29.6433333f));
777       timeZoneGeographicPoints.put("Europe/Uzhgorod", new GeographicPoint(48.6166667f, 22.3f));
778       timeZoneGeographicPoints.put("Europe/Vilnius", new GeographicPoint(54.6833333f, 25.3166667f));
779       timeZoneGeographicPoints.put("Europe/Zaporozhye", new GeographicPoint(47.833f, 35.1667f));
780       timeZoneGeographicPoints.put("Africa/Addis_Ababa", new GeographicPoint(9.0333333f, 38.7f));
781       timeZoneGeographicPoints.put("Africa/Asmara", new GeographicPoint(15.3333333f, 38.9333333f));
782       timeZoneGeographicPoints.put("Africa/Asmera", new GeographicPoint(15.3333333f, 38.9333333f));
783       timeZoneGeographicPoints.put("Africa/Dar_es_Salaam", new GeographicPoint(-6.8f, 39.2833333f));
784       timeZoneGeographicPoints.put("Africa/Djibouti", new GeographicPoint(11.595f, 43.1480556f));
785       timeZoneGeographicPoints.put("Africa/Kampala", new GeographicPoint(0.3155556f, 32.5655556f));
786       timeZoneGeographicPoints.put("Africa/Khartoum", new GeographicPoint(15.5880556f, 32.5341667f));
787       timeZoneGeographicPoints.put("Africa/Mogadishu", new GeographicPoint(2.0666667f, 45.3666667f));
788       timeZoneGeographicPoints.put("Africa/Nairobi", new GeographicPoint(-1.2833333f, 36.8166667f));
789       timeZoneGeographicPoints.put("Antarctica/Syowa", new GeographicPoint(-69f, 39.5833f));
790       timeZoneGeographicPoints.put("Asia/Aden", new GeographicPoint(12.7794444f, 45.0366667f));
791       timeZoneGeographicPoints.put("Asia/Baghdad", new GeographicPoint(33.3386111f, 44.3938889f));
792       timeZoneGeographicPoints.put("Asia/Bahrain", new GeographicPoint(26.2361111f, 50.5830556f)); // Manama
793       timeZoneGeographicPoints.put("Asia/Kuwait", new GeographicPoint(29.3697222f, 47.9783333f));
794       timeZoneGeographicPoints.put("Asia/Qatar", new GeographicPoint(25.2866667f, 51.5333333f)); // Doha
795       timeZoneGeographicPoints.put("Asia/Riyadh", new GeographicPoint(24.6408333f, 46.7727778f));
796       GeographicPoint moscow = new GeographicPoint(55.7522222f, 37.6155556f);
797       timeZoneGeographicPoints.put("Etc/GMT-3", moscow); // Moscow
798       timeZoneGeographicPoints.put("Europe/Moscow", moscow);
799       timeZoneGeographicPoints.put("Europe/Volgograd", new GeographicPoint(48.8047222f, 44.5858333f));
800       timeZoneGeographicPoints.put("Indian/Antananarivo", new GeographicPoint(-18.9166667f, 47.5166667f));
801       timeZoneGeographicPoints.put("Indian/Comoro", new GeographicPoint(-11.7041667f, 43.2402778f)); // Moroni
802       timeZoneGeographicPoints.put("Indian/Mayotte", new GeographicPoint(-12.7794444f, 45.2272222f)); // Mamoudzou
803       timeZoneGeographicPoints.put("Asia/Riyadh87", new GeographicPoint(24.6408333f, 46.7727778f));
804       timeZoneGeographicPoints.put("Asia/Riyadh88", new GeographicPoint(24.6408333f, 46.7727778f));
805       timeZoneGeographicPoints.put("Asia/Riyadh89", new GeographicPoint(24.6408333f, 46.7727778f));
806       timeZoneGeographicPoints.put("Mideast/Riyadh87", new GeographicPoint(24.6408333f, 46.7727778f));
807       timeZoneGeographicPoints.put("Mideast/Riyadh88", new GeographicPoint(24.6408333f, 46.7727778f));
808       timeZoneGeographicPoints.put("Mideast/Riyadh89", new GeographicPoint(24.6408333f, 46.7727778f));
809       timeZoneGeographicPoints.put("Asia/Tehran", new GeographicPoint(35.6719444f, 51.4244444f));
810       timeZoneGeographicPoints.put("Asia/Baku", new GeographicPoint(40.3952778f, 49.8822222f));
811       GeographicPoint dubai = new GeographicPoint(25.2522222f, 55.28f);
812       timeZoneGeographicPoints.put("Asia/Dubai", dubai);
813       timeZoneGeographicPoints.put("Asia/Muscat", new GeographicPoint(23.6133333f, 58.5933333f));
814       timeZoneGeographicPoints.put("Asia/Tbilisi", new GeographicPoint(41.725f, 44.7908333f));
815       timeZoneGeographicPoints.put("Asia/Yerevan", new GeographicPoint(40.1811111f, 44.5136111f));
816       timeZoneGeographicPoints.put("Etc/GMT-4", dubai); // Dubai
817       timeZoneGeographicPoints.put("Europe/Samara", new GeographicPoint(53.2f, 50.15f));
818       timeZoneGeographicPoints.put("Indian/Mahe", new GeographicPoint(-4.6166667f, 55.45f));
819       timeZoneGeographicPoints.put("Indian/Mauritius", new GeographicPoint(-20.1619444f, 57.4988889f)); // Port Louis
820       timeZoneGeographicPoints.put("Indian/Reunion", new GeographicPoint(-20.8666667f, 55.4666667f)); // Saint Denis
821       timeZoneGeographicPoints.put("Asia/Kabul", new GeographicPoint(34.5166667f, 69.1833333f));
822       timeZoneGeographicPoints.put("Antarctica/Davis", new GeographicPoint(-68.5764f, 77.9689f));
823       timeZoneGeographicPoints.put("Antarctica/Mawson", new GeographicPoint(-53.104f, 73.514f));
824       timeZoneGeographicPoints.put("Asia/Aqtau", new GeographicPoint(43.65f, 51.2f));
825       timeZoneGeographicPoints.put("Asia/Aqtobe", new GeographicPoint(50.2980556f, 57.1813889f));
826       timeZoneGeographicPoints.put("Asia/Ashgabat", new GeographicPoint(37.95f, 58.3833333f));
827       timeZoneGeographicPoints.put("Asia/Ashkhabad", new GeographicPoint(37.95f, 58.3833333f));
828       timeZoneGeographicPoints.put("Asia/Dushanbe", new GeographicPoint(38.56f, 68.7738889f));
829       timeZoneGeographicPoints.put("Asia/Karachi", new GeographicPoint(24.8666667f, 67.05f));
830       timeZoneGeographicPoints.put("Asia/Oral", new GeographicPoint(51.2333333f, 51.3666667f));
831       timeZoneGeographicPoints.put("Asia/Samarkand", new GeographicPoint(39.6541667f, 66.9597222f));
832       timeZoneGeographicPoints.put("Asia/Tashkent", new GeographicPoint(41.3166667f, 69.25f));
833       timeZoneGeographicPoints.put("Asia/Yekaterinburg", new GeographicPoint(56.85f, 60.6f));
834       GeographicPoint calcutta = new GeographicPoint(22.569722f, 88.369722f);
835       timeZoneGeographicPoints.put("Etc/GMT-5", calcutta); // Calcutta
836       timeZoneGeographicPoints.put("Indian/Kerguelen", new GeographicPoint(-49.25f, 69.583f)); // Port-aux-Francais
837       timeZoneGeographicPoints.put("Indian/Maldives", new GeographicPoint(4.1666667f, 73.5f)); // Male
838       timeZoneGeographicPoints.put("Asia/Calcutta", calcutta);
839       timeZoneGeographicPoints.put("Asia/Colombo", new GeographicPoint(6.9319444f, 79.8477778f));
840       timeZoneGeographicPoints.put("Asia/Kolkata", calcutta);
841       timeZoneGeographicPoints.put("Asia/Kathmandu", new GeographicPoint(27.7166667f, 85.3166667f));
842       timeZoneGeographicPoints.put("Asia/Katmandu", new GeographicPoint(27.7166667f, 85.3166667f));
843       timeZoneGeographicPoints.put("Antarctica/Vostok", new GeographicPoint(-78.4644f, 106.8372f));
844       timeZoneGeographicPoints.put("Asia/Almaty", new GeographicPoint(43.25f, 76.95f));
845       timeZoneGeographicPoints.put("Asia/Bishkek", new GeographicPoint(42.8730556f, 74.6002778f));
846       GeographicPoint dacca = new GeographicPoint(23.7230556f, 90.4086111f);
847       timeZoneGeographicPoints.put("Asia/Dacca", dacca);
848       timeZoneGeographicPoints.put("Asia/Dhaka", dacca);
849       timeZoneGeographicPoints.put("Asia/Novokuznetsk", new GeographicPoint(53.75f, 87.1f));
850       timeZoneGeographicPoints.put("Asia/Novosibirsk", new GeographicPoint(55.0411111f, 82.9344444f));
851       timeZoneGeographicPoints.put("Asia/Omsk", new GeographicPoint(55.0f, 73.4f));
852       timeZoneGeographicPoints.put("Asia/Qyzylorda", new GeographicPoint(44.8527778f, 65.5091667f));
853       timeZoneGeographicPoints.put("Asia/Thimbu", new GeographicPoint(27.4833333f, 89.6f));
854       timeZoneGeographicPoints.put("Asia/Thimphu", new GeographicPoint(27.4833333f, 89.6f));
855       timeZoneGeographicPoints.put("Etc/GMT-6", dacca); // Dacca
856       timeZoneGeographicPoints.put("Indian/Chagos", new GeographicPoint(-6f, 71.5f));
857       timeZoneGeographicPoints.put("Asia/Rangoon", new GeographicPoint(16.7833333f, 96.1666667f));
858       timeZoneGeographicPoints.put("Indian/Cocos", new GeographicPoint(-12.1167f, 96.9f));
859       GeographicPoint bangkok = new GeographicPoint(13.75f, 100.516667f);
860       timeZoneGeographicPoints.put("Asia/Bangkok", bangkok);
861       timeZoneGeographicPoints.put("Asia/Ho_Chi_Minh", new GeographicPoint(10.75f, 106.6666667f));
862       timeZoneGeographicPoints.put("Asia/Hovd", new GeographicPoint(48.0166667f, 91.6333333f));
863       timeZoneGeographicPoints.put("Asia/Jakarta", new GeographicPoint(-6.1744444f, 106.8294444f));
864       timeZoneGeographicPoints.put("Asia/Krasnoyarsk", new GeographicPoint(56.0097222f, 92.7916667f));
865       timeZoneGeographicPoints.put("Asia/Phnom_Penh", new GeographicPoint(11.55f, 104.9166667f));
866       timeZoneGeographicPoints.put("Asia/Pontianak", new GeographicPoint(-0.0333333f, 109.3333333f));
867       timeZoneGeographicPoints.put("Asia/Saigon", new GeographicPoint(10.75f, 106.6666667f));
868       timeZoneGeographicPoints.put("Asia/Vientiane", new GeographicPoint(17.966667f, 102.6f));
869       timeZoneGeographicPoints.put("Etc/GMT-7", bangkok); // Bangkok
870       timeZoneGeographicPoints.put("Indian/Christmas", new GeographicPoint(-10.4166667f, 105.7166667f)); // Flying Fish Cove
871       timeZoneGeographicPoints.put("Asia/Brunei", new GeographicPoint(4.8833333f, 114.9333333f));
872       timeZoneGeographicPoints.put("Asia/Choibalsan", new GeographicPoint(48.0666667f, 114.5f));
873       timeZoneGeographicPoints.put("Asia/Chongqing", new GeographicPoint(29.5627778f, 106.5527778f));
874       timeZoneGeographicPoints.put("Asia/Chungking", new GeographicPoint(29.5627778f, 106.5527778f));
875       timeZoneGeographicPoints.put("Asia/Harbin", new GeographicPoint(45.75f, 126.65f));
876       timeZoneGeographicPoints.put("Asia/Hong_Kong", new GeographicPoint(22.2833333f, 114.15f));
877       timeZoneGeographicPoints.put("Asia/Irkutsk", new GeographicPoint(52.2666667f, 104.3333333f));
878       timeZoneGeographicPoints.put("Asia/Kashgar", new GeographicPoint(39.3913889f, 76.04f));
879       timeZoneGeographicPoints.put("Asia/Kuala_Lumpur", new GeographicPoint(3.1666667f, 101.7f));
880       timeZoneGeographicPoints.put("Asia/Kuching", new GeographicPoint(1.55f, 110.3333333f));
881       timeZoneGeographicPoints.put("Asia/Macao", new GeographicPoint(22.2f, 113.55f));
882       timeZoneGeographicPoints.put("Asia/Macau", new GeographicPoint(22.2f, 113.55f));
883       timeZoneGeographicPoints.put("Asia/Makassar", new GeographicPoint(2.45f, 99.7833333f));
884       timeZoneGeographicPoints.put("Asia/Manila", new GeographicPoint(14.6041667f, 120.9822222f));
885       GeographicPoint shanghai = new GeographicPoint(31.005f, 121.4086111f);
886       timeZoneGeographicPoints.put("Asia/Shanghai", shanghai);
887       timeZoneGeographicPoints.put("Asia/Singapore", new GeographicPoint(1.2930556f, 103.8558333f));
888       timeZoneGeographicPoints.put("Asia/Taipei", new GeographicPoint(25.0391667f, 121.525f));
889       timeZoneGeographicPoints.put("Asia/Ujung_Pandang", new GeographicPoint(-5.1305556f, 119.4069444f));
890       timeZoneGeographicPoints.put("Asia/Ulaanbaatar", new GeographicPoint(47.9166667f, 106.9166667f));
891       timeZoneGeographicPoints.put("Asia/Ulan_Bator", new GeographicPoint(47.9166667f, 106.9166667f));
892       timeZoneGeographicPoints.put("Asia/Urumqi", new GeographicPoint(43.8f, 87.5833333f));
893       timeZoneGeographicPoints.put("Australia/Perth", new GeographicPoint(-31.933333f, 115.833333f));
894       timeZoneGeographicPoints.put("Australia/West", new GeographicPoint(-31.933333f, 115.833333f)); // Perth
895       timeZoneGeographicPoints.put("Etc/GMT-8", shanghai); // Shanghai
896       timeZoneGeographicPoints.put("Australia/Eucla", new GeographicPoint(-31.716667f, 128.866667f));
897       timeZoneGeographicPoints.put("Asia/Dili", new GeographicPoint(-8.55f, 125.5833f));
898       timeZoneGeographicPoints.put("Asia/Jayapura", new GeographicPoint(-2.5333333f, 140.7f));
899       timeZoneGeographicPoints.put("Asia/Pyongyang", new GeographicPoint(39.0194444f, 125.7547222f));
900       timeZoneGeographicPoints.put("Asia/Seoul", new GeographicPoint(37.5663889f, 126.9997222f));
901       GeographicPoint tokyo = new GeographicPoint(35.685f, 139.7513889f);
902       timeZoneGeographicPoints.put("Asia/Tokyo", tokyo);
903       timeZoneGeographicPoints.put("Asia/Yakutsk", new GeographicPoint(62.0338889f, 129.7330556f));
904       timeZoneGeographicPoints.put("Etc/GMT-9", tokyo); // Tokyo
905       timeZoneGeographicPoints.put("Pacific/Palau", new GeographicPoint(7.5f, 134.6241f)); // Ngerulmud
906       timeZoneGeographicPoints.put("Australia/Adelaide", new GeographicPoint(-34.933333f, 138.6f));
907       timeZoneGeographicPoints.put("Australia/Broken_Hill", new GeographicPoint(-31.95f, 141.433333f));
908       timeZoneGeographicPoints.put("Australia/Darwin", new GeographicPoint(-12.466667f, 130.833333f));
909       timeZoneGeographicPoints.put("Australia/North", new GeographicPoint(-12.466667f, 130.833333f)); // Darwin
910       timeZoneGeographicPoints.put("Australia/South", new GeographicPoint(-34.933333f, 138.6f)); // Adelaide
911       timeZoneGeographicPoints.put("Australia/Yancowinna", new GeographicPoint(-31.7581f, 141.7178f));
912       timeZoneGeographicPoints.put("Antarctica/DumontDUrville", new GeographicPoint(-66.66277f, 140.0014f));
913       timeZoneGeographicPoints.put("Asia/Sakhalin", new GeographicPoint(51f, 143f));
914       timeZoneGeographicPoints.put("Asia/Vladivostok", new GeographicPoint(43.1333333f, 131.9f));
915       timeZoneGeographicPoints.put("Australia/ACT", new GeographicPoint(-35.283333f, 149.216667f)); // Canberra
916       timeZoneGeographicPoints.put("Australia/Brisbane", new GeographicPoint(-27.5f, 153.016667f));
917       timeZoneGeographicPoints.put("Australia/Canberra", new GeographicPoint(-35.283333f, 149.216667f));
918       timeZoneGeographicPoints.put("Australia/Currie", new GeographicPoint(-39.933333f, 143.866667f));
919       timeZoneGeographicPoints.put("Australia/Hobart", new GeographicPoint(-42.916667f, 147.333333f));
920       timeZoneGeographicPoints.put("Australia/Lindeman", new GeographicPoint(-20.45f, 149.0333f));
921       timeZoneGeographicPoints.put("Australia/Melbourne", new GeographicPoint(-37.816667f, 144.966667f));
922       GeographicPoint sydney = new GeographicPoint(-33.883333f, 151.216667f);
923       timeZoneGeographicPoints.put("Australia/NSW", sydney); // Sydney
924       timeZoneGeographicPoints.put("Australia/Queensland", new GeographicPoint(-27.5f, 153.016667f)); // Brisbane
925       timeZoneGeographicPoints.put("Australia/Sydney", sydney);
926       timeZoneGeographicPoints.put("Australia/Tasmania", new GeographicPoint(-42.916667f, 147.333333f)); // Hobart
927       timeZoneGeographicPoints.put("Australia/Victoria", new GeographicPoint(-37.816667f, 144.966667f)); // Melbourne
928       timeZoneGeographicPoints.put("Etc/GMT-10", sydney); // Sydney
929       timeZoneGeographicPoints.put("Pacific/Guam", new GeographicPoint(13.467f, 144.75f)); // Hagatna
930       timeZoneGeographicPoints.put("Pacific/Port_Moresby", new GeographicPoint(-9.4647222f, 147.1925f));
931       timeZoneGeographicPoints.put("Pacific/Saipan", new GeographicPoint(15.1833f, 145.75f));
932       timeZoneGeographicPoints.put("Pacific/Truk", new GeographicPoint(7.4167f, 151.7833f));
933       timeZoneGeographicPoints.put("Pacific/Yap", new GeographicPoint(9.5144444f, 138.1291667f));
934       timeZoneGeographicPoints.put("Australia/LHI", new GeographicPoint(-31.55f, 159.083f));
935       timeZoneGeographicPoints.put("Australia/Lord_Howe", new GeographicPoint(-31.55f, 159.083f));
936       timeZoneGeographicPoints.put("Antarctica/Casey", new GeographicPoint(-66.2833f, 110.5333f));
937       timeZoneGeographicPoints.put("Asia/Magadan", new GeographicPoint(59.5666667f, 150.8f));
938       GeographicPoint noumea = new GeographicPoint(-22.2666667f, 166.45f);
939       timeZoneGeographicPoints.put("Etc/GMT-11", noumea); // Noumea
940       timeZoneGeographicPoints.put("Pacific/Efate", new GeographicPoint(-17.667f, 168.417f));
941       timeZoneGeographicPoints.put("Pacific/Guadalcanal", new GeographicPoint(-9.617f, 160.183f));
942       timeZoneGeographicPoints.put("Pacific/Kosrae", new GeographicPoint(5.317f, 162.983f));
943       timeZoneGeographicPoints.put("Pacific/Noumea", noumea);
944       timeZoneGeographicPoints.put("Pacific/Ponape", new GeographicPoint(6.9638889f, 158.2083333f));
945       timeZoneGeographicPoints.put("Pacific/Norfolk", new GeographicPoint(-29.05f, 167.95f)); // Kingston
946       timeZoneGeographicPoints.put("Antarctica/McMurdo", new GeographicPoint(-77.85f, 166.667f));
947       timeZoneGeographicPoints.put("Antarctica/South_Pole", new GeographicPoint(-90f, 0f));
948       timeZoneGeographicPoints.put("Asia/Anadyr", new GeographicPoint(64.75f, 177.4833333f));
949       timeZoneGeographicPoints.put("Asia/Kamchatka", new GeographicPoint(57f, 160f));
950       GeographicPoint auckland = new GeographicPoint(-36.866667f, 174.766667f);
951       timeZoneGeographicPoints.put("Etc/GMT-12", auckland); // Auckland
952       timeZoneGeographicPoints.put("Pacific/Auckland", auckland);
953       timeZoneGeographicPoints.put("Pacific/Fiji", new GeographicPoint(-18.1333333f, 178.4166667f)); // Suva
954       timeZoneGeographicPoints.put("Pacific/Funafuti", new GeographicPoint(-8.5166667f, 179.2166667f));
955       timeZoneGeographicPoints.put("Pacific/Kwajalein", new GeographicPoint(9.1939f, 167.4597f));
956       timeZoneGeographicPoints.put("Pacific/Majuro", new GeographicPoint(7.1f, 171.3833333f));
957       timeZoneGeographicPoints.put("Pacific/Nauru", new GeographicPoint(-0.5322f, 166.9328f));
958       timeZoneGeographicPoints.put("Pacific/Tarawa", new GeographicPoint(1.4167f, 173.0333f));
959       timeZoneGeographicPoints.put("Pacific/Wake", new GeographicPoint(19.2833f, 166.6f));
960       timeZoneGeographicPoints.put("Pacific/Wallis", new GeographicPoint(-13.273f, -176.205f));
961       timeZoneGeographicPoints.put("Pacific/Chatham", new GeographicPoint(-43.883f, -176.517f));
962       GeographicPoint enderbury = new GeographicPoint(-3.133f, -171.0833f);
963       timeZoneGeographicPoints.put("Etc/GMT-13", enderbury); // Enderbury
964       timeZoneGeographicPoints.put("Pacific/Enderbury", enderbury);
965       timeZoneGeographicPoints.put("Pacific/Tongatapu", new GeographicPoint(-21.2114f, -175.153f));
966       GeographicPoint kiritimati = new GeographicPoint(1.883f, -157.4f);
967       timeZoneGeographicPoints.put("Etc/GMT-14", kiritimati); // Kiritimati
968       timeZoneGeographicPoints.put("Pacific/Kiritimati", kiritimati);
969 
970       timeZoneGeographicPoints.put("MIT", apia); // Apia
971       timeZoneGeographicPoints.put("HST", honolulu); // Honolulu
972       timeZoneGeographicPoints.put("PST", losAngeles); // Los Angeles
973       timeZoneGeographicPoints.put("PST8PDT", losAngeles); // Los Angeles
974       timeZoneGeographicPoints.put("MST", denver); // Denver
975       timeZoneGeographicPoints.put("MST7MDT", denver); // Denver
976       timeZoneGeographicPoints.put("Navajo", new GeographicPoint(35.6728f, -109.0622f)); // Window Rock
977       timeZoneGeographicPoints.put("PNT", new GeographicPoint(33.4483333f, -112.0733333f)); // Phoenix
978       timeZoneGeographicPoints.put("America/Indiana/Knox", new GeographicPoint(41.2958333f, -86.6250000f));
979       timeZoneGeographicPoints.put("America/Indiana/Tell_City", new GeographicPoint(37.953f, -86.7614f));
980       timeZoneGeographicPoints.put("America/North_Dakota/Center", new GeographicPoint(47.115f, -101.3003f));
981       timeZoneGeographicPoints.put("America/North_Dakota/New_Salem", new GeographicPoint(46.843f, 101.4119f));
982       timeZoneGeographicPoints.put("CST", chicago); // Chicago
983       timeZoneGeographicPoints.put("CST6CDT", chicago); // Chicago
984       timeZoneGeographicPoints.put("America/Indiana/Indianapolis", new GeographicPoint(39.7683333f, -86.1580556f));
985       timeZoneGeographicPoints.put("America/Indiana/Marengo", new GeographicPoint(36.3706f, -86.3433f));
986       timeZoneGeographicPoints.put("America/Indiana/Petersburg", new GeographicPoint(38.4917f, -87.2803f));
987       timeZoneGeographicPoints.put("America/Indiana/Vevay", new GeographicPoint(38.7458f, -85.0711f));
988       timeZoneGeographicPoints.put("America/Indiana/Vincennes", new GeographicPoint(38.6783f, -87.5164f));
989       timeZoneGeographicPoints.put("America/Indiana/Winamac", new GeographicPoint(41.0525f, -86.6044f));
990       timeZoneGeographicPoints.put("America/Kentucky/Louisville", new GeographicPoint(38.2542f, -85.7603f));
991       timeZoneGeographicPoints.put("America/Kentucky/Monticello", new GeographicPoint(36.8381f, -84.85f));
992       timeZoneGeographicPoints.put("Cuba", new GeographicPoint(23.1319444f, -82.3641667f)); // Havana
993       timeZoneGeographicPoints.put("EST", newYork); // New York
994       timeZoneGeographicPoints.put("EST5EDT", newYork); // New York
995       timeZoneGeographicPoints.put("IET", newYork); // New York
996       timeZoneGeographicPoints.put("AST", new GeographicPoint(44.65f, -63.6f)); // Halifax
997       timeZoneGeographicPoints.put("Jamaica", new GeographicPoint(18.0f, -76.8f)); // Kingston
998       timeZoneGeographicPoints.put("America/Argentina/San_Luis", new GeographicPoint(-33.3f, -66.333f));
999       timeZoneGeographicPoints.put("PRT", new GeographicPoint(18.467f, 66.117f)); // San Juan
1000       timeZoneGeographicPoints.put("CNT", new GeographicPoint(47.5675f, -52.7072f)); // St John's
1001       timeZoneGeographicPoints.put("AGT", new GeographicPoint(-34.5875f, -58.6725f)); // Buenos Aires
1002       timeZoneGeographicPoints.put("America/Argentina/Buenos_Aires", new GeographicPoint(-34.5875f, -58.6725f));
1003       timeZoneGeographicPoints.put("America/Argentina/Catamarca", new GeographicPoint(-28.4666667f, -65.7833333f));
1004       timeZoneGeographicPoints.put("America/Argentina/ComodRivadavia", new GeographicPoint(-42.7578f, -65.0297f));
1005       timeZoneGeographicPoints.put("America/Argentina/Cordoba", new GeographicPoint(-31.4f, -64.1833333f));
1006       timeZoneGeographicPoints.put("America/Argentina/Jujuy", new GeographicPoint(-24.1833333f, -65.3f));
1007       timeZoneGeographicPoints.put("America/Argentina/La_Rioja", new GeographicPoint(-29.4144f, -66.8552f));
1008       timeZoneGeographicPoints.put("America/Argentina/Mendoza", new GeographicPoint(-32.8833333f, -68.8166667f));
1009       timeZoneGeographicPoints.put("America/Argentina/Rio_Gallegos", new GeographicPoint(-51.625f, -69.2286f));
1010       timeZoneGeographicPoints.put("America/Argentina/Salta", new GeographicPoint(-24.7833333f, -65.4166667f));
1011       timeZoneGeographicPoints.put("America/Argentina/San_Juan", new GeographicPoint(-31.5333f, -68.5167f));
1012       timeZoneGeographicPoints.put("America/Argentina/Tucuman", new GeographicPoint(-26.8167f, 65.2167f));
1013       timeZoneGeographicPoints.put("America/Argentina/Ushuaia", new GeographicPoint(-54.6f, -68.3f));
1014       timeZoneGeographicPoints.put("BET", saoPaulo); // Sao Paulo
1015       timeZoneGeographicPoints.put("Eire", new GeographicPoint(53.3330556f, -6.2488889f)); // Dublin
1016       timeZoneGeographicPoints.put("GB", greenwich); // Greenwich
1017       timeZoneGeographicPoints.put("GB-Eire", new GeographicPoint(53.3330556f, -6.2488889f)); // Dublin
1018       timeZoneGeographicPoints.put("GMT", greenwich); // Greenwich
1019       timeZoneGeographicPoints.put("GMT0", greenwich); // Greenwich
1020       timeZoneGeographicPoints.put("Greenwich", greenwich); // Greenwich
1021       timeZoneGeographicPoints.put("Iceland", new GeographicPoint(64.1333f, -21.9333f)); // Reykjav�k
1022       timeZoneGeographicPoints.put("Portugal", new GeographicPoint(38.7166667f, -9.1333333f)); // Lisbon
1023       timeZoneGeographicPoints.put("UCT", greenwich); // Greenwich
1024       timeZoneGeographicPoints.put("UTC", greenwich); // Greenwich
1025       timeZoneGeographicPoints.put("Universal", greenwich); // Greenwich
1026       timeZoneGeographicPoints.put("WET", greenwich); // Greenwich
1027       timeZoneGeographicPoints.put("Zulu", greenwich); // Greenwich
1028       timeZoneGeographicPoints.put("CET", paris); // Paris
1029       timeZoneGeographicPoints.put("ECT", paris); // Paris
1030       timeZoneGeographicPoints.put("MET", new GeographicPoint(35.6719444f, 51.4244444f)); // Tehran
1031       timeZoneGeographicPoints.put("Poland", new GeographicPoint(52.25f, 21.0f)); // Warsaw
1032       timeZoneGeographicPoints.put("ART", new GeographicPoint(-34.5875f, -58.6725f)); // Buenos Aires
1033       timeZoneGeographicPoints.put("CAT", new GeographicPoint(-1.9536111f, 30.0605556f)); // Kigali
1034       timeZoneGeographicPoints.put("EET", new GeographicPoint(37.9833333f, 23.7333333f)); // Athens
1035       timeZoneGeographicPoints.put("Egypt", new GeographicPoint(30.05f, 31.25f)); // Cairo
1036       timeZoneGeographicPoints.put("Israel", new GeographicPoint(32.0666667f, 34.7666667f)); // Tel Aviv
1037       timeZoneGeographicPoints.put("Libya", new GeographicPoint(32.8925f, 13.18f)); // Tripoli
1038       timeZoneGeographicPoints.put("Turkey", new GeographicPoint(41.0186111f, 28.9647222f)); // Istanbul
1039       timeZoneGeographicPoints.put("EAT", new GeographicPoint(-1.2833333f, 36.8166667f)); // Nairobi
1040       timeZoneGeographicPoints.put("W-SU", moscow); // Moscow
1041       timeZoneGeographicPoints.put("Iran", new GeographicPoint(35.6719444f, 51.4244444f)); // Tehran
1042       timeZoneGeographicPoints.put("NET", new GeographicPoint(40.1811111f, 44.5136111f)); // Yerevan
1043       timeZoneGeographicPoints.put("PLT", new GeographicPoint(24.8666667f, 67.05f)); // Karachi
1044       timeZoneGeographicPoints.put("IST", calcutta); // Calcutta
1045       timeZoneGeographicPoints.put("BST", dacca); // Dacca
1046       timeZoneGeographicPoints.put("VST", bangkok); // Bangkok
1047       timeZoneGeographicPoints.put("CTT", shanghai); // Shanghai
1048       timeZoneGeographicPoints.put("Hongkong", new GeographicPoint(22.2833333f, 114.15f));
1049       timeZoneGeographicPoints.put("PRC", shanghai); // Shanghai
1050       timeZoneGeographicPoints.put("Singapore", new GeographicPoint(1.2930556f, 103.8558333f));
1051       timeZoneGeographicPoints.put("JST", tokyo); // Tokyo
1052       timeZoneGeographicPoints.put("Japan", tokyo); // Tokyo
1053       timeZoneGeographicPoints.put("ROK", new GeographicPoint(37.5663889f, 126.9997222f)); // Seoul
1054       timeZoneGeographicPoints.put("ACT", new GeographicPoint(-35.283333f, 149.216667f)); // Canberra
1055       timeZoneGeographicPoints.put("AET", sydney); // Sydney
1056       timeZoneGeographicPoints.put("SST", new GeographicPoint(-28.4667f, 159.8167f)); // Honiara
1057       timeZoneGeographicPoints.put("Kwajalein", new GeographicPoint(9.1939f, 167.4597f));
1058       timeZoneGeographicPoints.put("NST", auckland); // Auckland
1059       timeZoneGeographicPoints.put("NZ", auckland); // Auckland
1060       timeZoneGeographicPoints.put("NZ-CHAT", new GeographicPoint(-43.883f, -176.517f)); // Chatham
1061 
1062       // Store geographic points in a weak reference because it should be used only to init a new compass
1063       timeZoneGeographicPointsReference = new WeakReference<Map<String,GeographicPoint>>(timeZoneGeographicPoints);
1064     }
1065 
1066     GeographicPoint point = timeZoneGeographicPoints.get(TimeZone.getDefault().getID());
1067     if (point == null) {
1068       point = timeZoneGeographicPoints.get("Etc/GMT");
1069     }
1070     this.latitude = (float)Math.toRadians(point.getLatitudeInDegrees());
1071     this.longitude = (float)Math.toRadians(point.getLongitudeInDegrees());
1072   }
1073 
1074   /**
1075    * A geographic point used to store known points.
1076    */
1077   private static class GeographicPoint {
1078     private final float latitudeInDegrees;
1079     private final float longitudeInDegrees;
1080 
GeographicPoint(float latitudeInDegrees, float longitudeInDegrees)1081     public GeographicPoint(float latitudeInDegrees, float longitudeInDegrees) {
1082       this.latitudeInDegrees = latitudeInDegrees;
1083       this.longitudeInDegrees = longitudeInDegrees;
1084     }
1085 
getLatitudeInDegrees()1086     public float getLatitudeInDegrees() {
1087       return this.latitudeInDegrees;
1088     }
1089 
getLongitudeInDegrees()1090     public float getLongitudeInDegrees() {
1091       return this.longitudeInDegrees;
1092     }
1093   }
1094 }
1095