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 */ loadCities(InputStream is)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 */ getCitiesByName()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 */ getCitiesByOffset()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 */ getCitiesByRawOffset()148 public static City[] getCitiesByRawOffset() { 149 return citiesByRawOffset; 150 } 151 152 /** 153 * Returns an Iterator over all cities, in raw offset order. 154 */ iterator()155 public static Iterator<City> iterator() { 156 return cities.values().iterator(); 157 } 158 159 /** 160 * Returns the total number of cities. 161 */ numCities()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 */ City(String name, String timeZoneID, int rawOffset, float latitude, float longitude, float x, float y, float z)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 getName()184 public String getName() { 185 return name; 186 } 187 getTimeZone()188 public TimeZone getTimeZone() { 189 if (timeZone == null) { 190 timeZone = TimeZone.getTimeZone(timeZoneID); 191 } 192 return timeZone; 193 } 194 getLongitude()195 public float getLongitude() { 196 return longitude; 197 } 198 getLatitude()199 public float getLatitude() { 200 return latitude; 201 } 202 getX()203 public float getX() { 204 return x; 205 } 206 getY()207 public float getY() { 208 return y; 209 } 210 getZ()211 public float getZ() { 212 return z; 213 } 214 getRawOffset()215 public float getRawOffset() { 216 return rawOffset / 3600000.0f; 217 } 218 getRawOffsetMillis()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 */ getOffset()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 */ compareTo(City o)238 public int compareTo(City o) { 239 return name.compareTo(o.name); 240 } 241 } 242