Source code for upoints.cities

#
# coding=utf-8
"""cities - Imports GNU miscfiles cities data files."""
# Copyright © 2007-2017  James Rowe <jnrowe@gmail.com>
#
# This file is part of upoints.
#
# upoints is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# upoints is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# upoints.  If not, see <http://www.gnu.org/licenses/>.

import logging
import time

from . import (point, trigpoints, utils)

#: GNU miscfiles cities.dat template
TEMPLATE = """\
ID          : %s
Type        : %s
Population  : %s
Size        : %s
Name        : %s
 Country    : %s
 Region     : %s
Location    : %s
 Longitude  : %s
 Latitude   : %s
 Elevation  : %s
Date        : %s
Entered-By  : %s"""


[docs]class City(trigpoints.Trigpoint): """Class for representing an entry from the `GNU miscfiles`_ cities data file. .. versionadded:: 0.2.0 .. _GNU miscfiles: http://directory.fsf.org/project/miscfiles/ """ __slots__ = ('identifier', 'ptype', 'population', 'size', 'country', 'region', 'location', 'date', 'entered') def __init__(self, identifier, name, ptype, region, country, location, population, size, latitude, longitude, altitude, date, entered): """Initialise a new ``City`` object. Args: identifier (int): Numeric identifier for object name (str): Place name ptype (str): Type of place region (str): Region place can be found country (str): Country name place can be found location (str): Body place can be found population (int): Place's population size (int): Place's area latitude (float): Station's latitude longitude (float): Station's longitude altitude (int): Station's elevation date (time.struct_time): Date the entry was added entered (str): Entry's author """ super(City, self).__init__(latitude, longitude, altitude, name) self.identifier = identifier self.ptype = ptype self.region = region self.country = country self.location = location self.population = population self.size = size self.date = date self.entered = entered def __str__(self): """Pretty printed location string. Returns: str: Human readable string representation of ``City`` object """ values = map(utils.value_or_empty, (self.identifier, self.ptype, self.population, self.size, self.name, self.country, self.region, self.location, self.latitude, self.longitude, self.altitude, time.strftime('%Y%m%d', self.date) if self.date else '', self.entered)) return TEMPLATE % tuple(values)
[docs]class Cities(point.Points): """Class for representing a group of :class:`City` objects. .. versionadded:: 0.5.1 """ def __init__(self, data=None): """Initialise a new ``Cities`` object.""" super(Cities, self).__init__() self._data = data if data: self.import_locations(data)
[docs] def import_locations(self, data): """Parse `GNU miscfiles`_ cities data files. ``import_locations()`` returns a list containing :class:`City` objects. It expects data files in the same format that `GNU miscfiles`_ provides, that is:: ID : 1 Type : City Population : 210700 Size : Name : Aberdeen Country : UK Region : Scotland Location : Earth Longitude : -2.083 Latitude : 57.150 Elevation : Date : 19961206 Entered-By : Rob.Hooft@EMBL-Heidelberg.DE // ID : 2 Type : City Population : 1950000 Size : Name : Abidjan Country : Ivory Coast Region : Location : Earth Longitude : -3.867 Latitude : 5.333 Elevation : Date : 19961206 Entered-By : Rob.Hooft@EMBL-Heidelberg.DE When processed by ``import_locations()`` will return ``list`` object in the following style:: [City(1, "City", 210700, None, "Aberdeen", "UK", "Scotland", "Earth", -2.083, 57.15, None, (1996, 12, 6, 0, 0, 0, 4, 341, -1), "Rob.Hooft@EMBL-Heidelberg.DE"), City(2, "City", 1950000, None, "Abidjan", "Ivory Coast", "", "Earth", -3.867, 5.333, None, (1996, 12, 6, 0, 0, 0, 4, 341, -1), "Rob.Hooft@EMBL-Heidelberg.DE")]) Args: data (iter): :abbr:`NOAA (National Oceanographic and Atmospheric Administration)` station data to read Returns: list: Places as ``City`` objects Raises: TypeError: Invalid value for data .. _GNU miscfiles: http://directory.fsf.org/project/miscfiles/ """ self._data = data if hasattr(data, 'read'): data = data.read().split('//\n') elif isinstance(data, list): pass elif isinstance(data, basestring): data = open(data).read().split('//\n') else: raise TypeError('Unable to handle data of type %r' % type(data)) keys = ('identifier', 'ptype', 'population', 'size', 'name', 'country', 'region', 'location', 'longitude', 'latitude', 'altitude', 'date', 'entered') for record in data: # We truncate after splitting because the v1.4.2 datafile contains # a broken separator between 229 and 230 that would otherwise break # the import data = [i.split(':')[1].strip() for i in record.splitlines()[:13]] entries = dict(zip(keys, data)) # Entry for Utrecht has the incorrect value of 0.000 for elevation. if entries['altitude'] == '0.000': logging.debug("Ignoring `0.000' value for elevation in %r " 'entry' % record) entries['altitude'] = '' for i in ('identifier', 'population', 'size', 'altitude'): entries[i] = int(entries[i]) if entries[i] else None for i in ('longitude', 'latitude'): entries[i] = float(entries[i]) if entries[i] else None entries['date'] = time.strptime(entries['date'], '%Y%m%d') self.append(City(**entries))