Getting the attributes' values

Let's explore the attributes of the world borders to find out why we were unable to get the names.

  1. Edit the if __name__ == '__main__': block:
    if __name__ == '__main__':
        world = BoundaryCollection("../data/world_borders_simple.shp")
        print(world.data[0].attributes.keys())
  2. Run the code and look at the output:
    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.

  3. Instead of accessing the attributes directly, let's make a method that will do it for us. Edit the 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.

  4. Now, edit the Boundary class to use the new method:
    class Boundary(BaseGeoObject):
        """Represents a single geographic boundary."""
        def __repr__(self):
            return self.get_attribute('name')
  5. Edit the if __name__ == '__main__': block:
    if __name__ == '__main__':    
        world = BoundaryCollection("../data/world_borders_simple.shp")
        for item in world.data:
            print(item)
  6. Run the code again. Now, you should have a beautiful list of country names:
    File imported: ../data/world_borders_simple.shp
    Antigua and Barbuda
    Algeria
    Azerbaijan
    Albania
    Armenia
    ...
    Process finished with exit code 0
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.131.38.14