Home | History | Annotate | Download | only in globaltime
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.globaltime;
     18 
     19 import java.io.DataInputStream;
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.util.Arrays;
     23 import java.util.Comparator;
     24 import java.util.HashMap;
     25 import java.util.Iterator;
     26 import java.util.Map;
     27 import java.util.TimeZone;
     28 
     29 /**
     30  * A class representing a city, with an associated position, time zone name,
     31  * and raw offset from UTC.
     32  */
     33 public  class City implements Comparable<City> {
     34 
     35     private static Map<String,City> cities = new HashMap<String,City>();
     36     private static City[] citiesByRawOffset;
     37 
     38     private String name;
     39     private String timeZoneID;
     40     private TimeZone timeZone = null;
     41     private int rawOffset;
     42     private float latitude, longitude;
     43     private float x, y, z;
     44 
     45     /**
     46      * Loads the city database.  The cities must be stored in order by raw
     47      * offset from UTC.
     48      */
     49     public static void loadCities(InputStream is) throws IOException {
     50         DataInputStream dis = new DataInputStream(is);
     51         int numCities = dis.readInt();
     52         citiesByRawOffset = new City[numCities];
     53 
     54         byte[] buf = new byte[24];
     55         for (int i = 0; i < numCities; i++) {
     56             String name = dis.readUTF();
     57             String tzid = dis.readUTF();
     58             dis.read(buf);
     59 
     60 //          The code below is a faster version of:
     61 //          int rawOffset = dis.readInt();
     62 //          float latitude = dis.readFloat();
     63 //          float longitude = dis.readFloat();
     64 //          float cx = dis.readFloat();
     65 //          float cy = dis.readFloat();
     66 //          float cz = dis.readFloat();
     67 
     68             int rawOffset =
     69                        (buf[ 0] << 24) |       ((buf[ 1] & 0xff) << 16) |
     70                       ((buf[ 2] & 0xff) << 8) | (buf[ 3] & 0xff);
     71             int ilat = (buf[ 4] << 24) |       ((buf[ 5] & 0xff) << 16) |
     72                       ((buf[ 6] & 0xff) << 8) | (buf[ 7] & 0xff);
     73             int ilon = (buf[ 8] << 24) |       ((buf[ 9] & 0xff) << 16) |
     74                       ((buf[10] & 0xff) << 8) | (buf[11] & 0xff);
     75             int icx =  (buf[12] << 24) |       ((buf[13] & 0xff) << 16) |
     76                       ((buf[14] & 0xff) << 8) | (buf[15] & 0xff);
     77             int icy =  (buf[16] << 24) |       ((buf[17] & 0xff) << 16) |
     78                       ((buf[18] & 0xff) << 8) | (buf[19] & 0xff);
     79             int icz =  (buf[20] << 24) |       ((buf[21] & 0xff) << 16) |
     80                       ((buf[22] & 0xff) << 8) | (buf[23] & 0xff);
     81             float latitude = Float.intBitsToFloat(ilat);
     82             float longitude = Float.intBitsToFloat(ilon);
     83             float cx = Float.intBitsToFloat(icx);
     84             float cy = Float.intBitsToFloat(icy);
     85             float cz = Float.intBitsToFloat(icz);
     86 
     87             City city = new City(name, tzid, rawOffset,
     88                                  latitude, longitude, cx, cy, cz);
     89 
     90             cities.put(name, city);
     91             citiesByRawOffset[i] = city;
     92         }
     93     }
     94 
     95     /**
     96      * Returns the cities, ordered by name.
     97      */
     98     public static City[] getCitiesByName() {
     99         City[] ocities = new City[cities.size()];
    100         Iterator<City> iter = cities.values().iterator();
    101         int idx = 0;
    102         while (iter.hasNext()) {
    103             ocities[idx++] = iter.next();
    104         }
    105         Arrays.sort(ocities);
    106         return ocities;
    107     }
    108 
    109     /**
    110      * Returns the cities, ordered by offset, accounting for summer/daylight
    111      * savings time.  This requires reading the entire time zone database
    112      * behind the scenes.
    113      */
    114     public static City[] getCitiesByOffset() {
    115         City[] ocities = new City[cities.size()];
    116         Iterator<City> iter = cities.values().iterator();
    117         int idx = 0;
    118         while (iter.hasNext()) {
    119             ocities[idx++] = iter.next();
    120         }
    121         Arrays.sort(ocities, new Comparator() {
    122                 public int compare(Object o1, Object o2) {
    123                     long now = System.currentTimeMillis();
    124                     City c1 = (City)o1;
    125                     City c2 = (City)o2;
    126                     TimeZone tz1 = c1.getTimeZone();
    127                     TimeZone tz2 = c2.getTimeZone();
    128                     int off1 = tz1.getOffset(now);
    129                     int off2 = tz2.getOffset(now);
    130                     if (off1 == off2) {
    131                         float dlat = c2.getLatitude() - c1.getLatitude();
    132                         if (dlat < 0.0f) return -1;
    133                         if (dlat > 0.0f) return 1;
    134                         return 0;
    135                     }
    136                     return off1 - off2;
    137                 }
    138             });
    139         return ocities;
    140     }
    141 
    142 
    143     /**
    144      * Returns the cities, ordered by offset, accounting for summer/daylight
    145      * savings time.  This does not require reading the time zone database
    146      * since the cities are pre-sorted.
    147      */
    148     public static City[] getCitiesByRawOffset() {
    149         return citiesByRawOffset;
    150     }
    151 
    152     /**
    153      * Returns an Iterator over all cities, in raw offset order.
    154      */
    155     public static Iterator<City> iterator() {
    156         return cities.values().iterator();
    157     }
    158 
    159     /**
    160      * Returns the total number of cities.
    161      */
    162     public static int numCities() {
    163         return cities.size();
    164     }
    165 
    166     /**
    167      * Constructs a city with the given name, time zone name, raw offset,
    168      * latitude, longitude, and 3D (X, Y, Z) coordinate.
    169      */
    170     public City(String name, String timeZoneID,
    171                 int rawOffset,
    172                 float latitude, float longitude,
    173                 float x, float y, float z) {
    174         this.name = name;
    175         this.timeZoneID = timeZoneID;
    176         this.rawOffset = rawOffset;
    177         this.latitude = latitude;
    178         this.longitude = longitude;
    179         this.x = x;
    180         this.y = y;
    181         this.z = z;
    182     }
    183 
    184     public String getName() {
    185         return name;
    186     }
    187 
    188     public TimeZone getTimeZone() {
    189         if (timeZone == null) {
    190             timeZone = TimeZone.getTimeZone(timeZoneID);
    191         }
    192         return timeZone;
    193     }
    194 
    195     public float getLongitude() {
    196         return longitude;
    197     }
    198 
    199     public float getLatitude() {
    200         return latitude;
    201     }
    202 
    203     public float getX() {
    204         return x;
    205     }
    206 
    207     public float getY() {
    208         return y;
    209     }
    210 
    211     public float getZ() {
    212         return z;
    213     }
    214 
    215     public float getRawOffset() {
    216         return rawOffset / 3600000.0f;
    217     }
    218 
    219     public int getRawOffsetMillis() {
    220         return rawOffset;
    221     }
    222 
    223     /**
    224      * Returns this city's offset from UTC, taking summer/daylight savigns
    225      * time into account.
    226      */
    227     public float getOffset() {
    228         long now = System.currentTimeMillis();
    229         if (timeZone == null) {
    230             timeZone = TimeZone.getTimeZone(timeZoneID);
    231         }
    232         return timeZone.getOffset(now) / 3600000.0f;
    233     }
    234 
    235     /**
    236      * Compares this city to another by name.
    237      */
    238     public int compareTo(City o) {
    239         return name.compareTo(o.name);
    240     }
    241 }
    242