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