Let's explore the attributes of the world borders to find out why we were unable to get the names.
if __name__ == '__main__':
block:if __name__ == '__main__': world = BoundaryCollection("../data/world_borders_simple.shp") print(world.data[0].attributes.keys())
File imported: ../data/world_borders_simple.shp ['SUBREGION', 'POP2005', 'REGION', 'ISO3', 'ISO2', 'FIPS', 'UN', 'NAME'] Process finished with exit code 0
What we did was we got the first item in world.data
and then printed its attribute keys. The list shown in the output has a NAME
key, but it is all in the uppercase. This is very common for Shapefiles whose data is contained in the DBF files.
Since we don't want to worry if the attributes' names are in the uppercase or lowercase, we have two possible solutions: convert the names at the moment of the import or convert the names on the fly when the attribute value is requested. Depending on your application, you may achieve better performance with one or the other method. Here, for didactic purposes, we will opt for the on-the-fly conversion and add a little spice to it.
BaseGeoObject
class' __init__
method and also add a get_attribute
method:class BaseGeoObject(object): """Base class for a single geo object.""" def __init__(self, geometry, attributes=None): self.geom = geometry self.attributes = attributes # Makes a lookup table of case insensitive attributes. self._attributes_lowercase = {} for key in self.attributes.keys(): self._attributes_lowercase[key.lower()] = key @property def coordinates(self): raise NotImplementedError def get_attribute(self, attr_name, case_sensitive=False): """Gets an attribute by its name. :param attr_name: The name of the attribute. :param case_sensitive: True or False. """ if not case_sensitive: attr_name = attr_name.lower() attr_name = self._attributes_lowercase[attr_name] return self.attributes[attr_name] def __repr__(self): raise NotImplementedError
In the __init__
method, we made a dictionary that contains the equivalence between lowercase attribute names and the original names. If you search the Internet, there is a number of techniques to implement case-insensitive dictionaries. But the one we implemented here allows us to preserve the original names, giving the user the option to choose whether he wants the search to be case-sensitive or not.
Boundary
class to use the new method:class Boundary(BaseGeoObject): """Represents a single geographic boundary.""" def __repr__(self): return self.get_attribute('name')
if __name__ == '__main__':
block:if __name__ == '__main__': world = BoundaryCollection("../data/world_borders_simple.shp") for item in world.data: print(item)
File imported: ../data/world_borders_simple.shp Antigua and Barbuda Algeria Azerbaijan Albania Armenia ... Process finished with exit code 0
3.131.38.14