1 /* 2 * Copyright (C) 2020 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 package com.android.timezone.location.validation; 17 18 import com.google.common.geometry.S2LatLng; 19 20 import java.io.BufferedReader; 21 import java.io.File; 22 import java.io.FileInputStream; 23 import java.io.IOException; 24 import java.io.InputStreamReader; 25 import java.nio.charset.StandardCharsets; 26 import java.util.Objects; 27 import java.util.stream.Stream; 28 29 /** A support class for accessing a geonames "cities" file. */ 30 class CitiesFile { 31 32 /** The index of the value containing the city name. */ 33 private static final int NAME_INDEX = 1; 34 /** The index of the value containing the city latitude. */ 35 private static final int LATITUDE_INDEX = 4; 36 /** The index of the value containing the city longitude. */ 37 private static final int LONGITUDE_INDEX = 5; 38 /** The index of the value containing the city ISO 3166 alpha-2 country code. */ 39 private static final int ISO_COUNTRY_CODE_INDEX = 8; 40 /** The index of the value containing the time zone Olson ID. */ 41 private static final int POPULATION_INDEX = 14; 42 /** The index of the value containing the time zone Olson ID. */ 43 private static final int TIME_ZONE_ID_INDEX = 17; 44 45 /** An individual geonames city entry. */ 46 static class City { 47 48 private final String mName; 49 private final S2LatLng mLatLng; 50 private final String mIsoCountryCode; 51 private final int mPopulation; 52 private final String mTimeZoneId; 53 City(String name, S2LatLng latLng, String isoCountryCode, int population, String timeZoneId)54 City(String name, S2LatLng latLng, String isoCountryCode, int population, 55 String timeZoneId) { 56 this.mName = Objects.requireNonNull(name); 57 this.mLatLng = Objects.requireNonNull(latLng); 58 this.mIsoCountryCode = isoCountryCode; 59 this.mPopulation = population; 60 this.mTimeZoneId = timeZoneId; 61 } 62 getName()63 String getName() { 64 return mName; 65 } 66 getLatLng()67 S2LatLng getLatLng() { 68 return mLatLng; 69 } 70 getIsoCountryCode()71 String getIsoCountryCode() { 72 return mIsoCountryCode; 73 } 74 getPopulation()75 int getPopulation() { 76 return mPopulation; 77 } 78 getTimeZoneId()79 String getTimeZoneId() { 80 return mTimeZoneId; 81 } 82 } 83 CitiesFile()84 private CitiesFile() { 85 } 86 87 /** Creates a {@link Stream} of {@link City} objects from the specified file. */ read(File file)88 static Stream<City> read(File file) throws IOException { 89 BufferedReader reader = new BufferedReader( 90 new InputStreamReader( 91 new FileInputStream(file), StandardCharsets.UTF_8)); 92 return reader.lines().map(CitiesFile::createCity); 93 } 94 createCity(String s)95 private static City createCity(String s) { 96 String[] fields = s.split("\\t"); 97 String name = fields[NAME_INDEX]; 98 String lat = fields[LATITUDE_INDEX]; 99 String lng = fields[LONGITUDE_INDEX]; 100 S2LatLng latLng = S2LatLng.fromDegrees(Double.parseDouble(lat), Double.parseDouble(lng)); 101 102 String isoCountryCode = fields[ISO_COUNTRY_CODE_INDEX]; 103 if (isoCountryCode.isEmpty()) { 104 isoCountryCode = null; 105 } else { 106 isoCountryCode = isoCountryCode.toLowerCase(); 107 } 108 109 String populationString = fields[POPULATION_INDEX]; 110 int population = 0; 111 if (populationString.isEmpty()) { 112 System.out.println("No population found for " + name); 113 } else { 114 population = Integer.parseInt(populationString); 115 } 116 117 String timeZoneId = fields[TIME_ZONE_ID_INDEX]; 118 if (timeZoneId.isEmpty()) { 119 timeZoneId = null; 120 } 121 122 return new City(name, latLng, isoCountryCode, population, timeZoneId); 123 } 124 } 125