Exercise 2
Contents
Exercise 2#
Important
Please complete this exercise by the end of day on Thursday, 16 November, 2023 (the day before the next work session).
To start this assignment, accept the GitHub classroom
assignment, and clone your own
repository, e.g., in a CSC
Notebook
instance. Make sure you commit and push all changes you make (you can
revisit instructions on how to use git
and the JupyterLab git-plugin
on the website of the Geo-Python
course.
To preview the exercise without logging in, you can find the open course copy of the course’s GitHub repository at github.com/Automating-GIS-processes-II-2023/Exercise-2. Don’t attempt to commit changes to that repository, but rather work with your personal GitHub classroom copy (see above).
Hints#
Converting a pandas.DataFrame
into a geopandas.GeoDataFrame
#
Sometimes, we work with data that are in a non-spatial format (such as Excel
or CSV spreadsheets) but contain information on the location of records, for
instance, in columns for longitude and latitude values. While geopandas’s
read_file()
function can read some formats, often, the safest way is to use
pandas to read the data set and then convert it to a GeoDataFrame
.
Let’s assume, we read the following table using pandas.read_csv()
into a
variable df
:
df
longitude | latitude | |
---|---|---|
0 | 24.9557 | 60.1555 |
1 | 24.8353 | 60.1878 |
2 | 24.9587 | 60.2029 |
The geopandas.GeoDataFrame()
constructor accepts a pandas.DataFrame
as an
input, but it does not automatically fill the geometry
column. However, the
library comes with a handy helper function geopandas.points_from_xy()
. As we
all know, a spatial data set should always have a coordinate reference system
(CRS) defined; we can specify the CRS of the input data, here, too:
import geopandas
gdf = geopandas.GeoDataFrame(
df,
geometry=geopandas.points_from_xy(df.longitude, df.latitude),
crs="EPSG:4326"
)
gdf
longitude | latitude | geometry | |
---|---|---|---|
0 | 24.9557 | 60.1555 | POINT (24.95570 60.15550) |
1 | 24.8353 | 60.1878 | POINT (24.83530 60.18780) |
2 | 24.9587 | 60.2029 | POINT (24.95870 60.20290) |
Now, we have a ‘proper‘ GeoDataFrame
with which we can do all geospatial
operations we would want to do.
Creating a new geopandas.GeoDataFrame
: alternative 1#
Sometimes, it makes sense to start from scratch with an empty data set and gradually add records. Of course, this is also possible with geopandas’ data frames, that can then be saved as a new geopackage or shapefile.
First, create a completely empty GeoDataFrame
:
import geopandas
new_geodataframe = geopandas.GeoDataFrame()
Then, create shapely geometry objects and insert them into the data frame. To
insert a geometry object into the geometry
column, and a name into the name
column, in a newly added row, use:
import shapely.geometry
polygon = shapely.geometry.Polygon(
[
(24.9510, 60.1690),
(24.9510, 60.1698),
(24.9536, 60.1698),
(24.9536, 60.1690)
]
)
name = "Senaatintori"
new_geodataframe.loc[
len(new_geodataframe), # in which row,
["name", "geometry"] # in which columns to save values
] = [name, polygon]
new_geodataframe
name | geometry | |
---|---|---|
0 | Senaatintori | POLYGON ((24.95100 60.16900, 24.95100 60.16980... |
Before saving the newly created dataset, don’t forget to define a cartographic reference system for it. Otherwise, you will have trouble reusing the file in other programs:
new_geodataframe.crs = "EPSG:4326"
Hint
In the example above, we used the len(new_geodataframe)
as a row index
(which, in a newly created data frame is equivalent to the row number). Since
rows are counted from 0, the number of rows (length of data frame) is one
greater than the address of the last row. This expression, thus, always adds a
new row, independent of the actual length of the data frame.
Note, that, strictly speaking, the index is independent from the row number, but in newly created data frames there are identical.
Creating a new geopandas.GeoDataFrame
: alternative 2#
Often, it is more convenient, and more elegant, to first create a dictionary to collect data, that can then be converted into a data frame all at once.
For this, first define a dict
with the column names as keys, and empty list
s
as values:
data = {
"name": [],
"geometry": []
}
Then, fill the dict with data:
import shapely.geometry
data["name"].append("Senaatintori")
data["geometry"].append(
shapely.geometry.Polygon(
[
(24.9510, 60.1690),
(24.9510, 60.1698),
(24.9536, 60.1698),
(24.9536, 60.1690)
]
)
)
Finally, use this dictionary as input for a new GeoDataFrame
. Don’t forget to
specify a CRS:
new_geodataframe = geopandas.GeoDataFrame(data, crs="EPSG:4326")
new_geodataframe
name | geometry | |
---|---|---|
0 | Senaatintori | POLYGON ((24.95100 60.16900, 24.95100 60.16980... |
Note
These two approaches result in identical GeoDataFrame
s. Sometimes, one
technique is more convenient than the other. You should always evaluate
different ways of solving a problem, and find the most appropriate and efficient
solution (there is always more than one possible solution).