-
Notifications
You must be signed in to change notification settings - Fork 13
Pedro R. Andrade
This tutorial describes how to create Generalized Proximity Matrixes (GPM) within TerraME. GPM is based on the idea that Euclidean spaces are not enough to describe the relations that take place within the geographical space. For more information on GPM, see Aguiar et al. (2003); Modeling Spatial Relations by Generalized Proximity Matrices. Proceedings of V Brazilian Symposium in Geoinformatics (GeoInfo'03).
GPM is composed by a set of strategies that try to capture such spatial warp, computing
operations over sets of spatial data. In the next sections, we will describe the basic structure of the implementation and present some examples of creating proximity matrixes.
Before starting, we will read some spatial data available within gpm
package (documentation available here):
- a set of lines, representing
roads
; - a set of points, representing the center of
communities
; - a set of polygons, representing
farms
. - a set of
cells
, create from the farms using scriptcells.lua
available in the data directory ofgpm
package.
All them must use geometry = true
to load their geometries.
roads = CellularSpace{file = filePath("roads.shp", "gpm"), geometry = true}
communities = CellularSpace{file = filePath("communities.shp", "gpm"), geometry = true}
farms = CellularSpace{file = filePath("farms.shp", "gpm"), geometry = true}
cells = CellularSpace{file = filePath("cells.shp", "gpm"), geometry = true}
Figure below shows them.
This section presents the strategies that use euclidean spaces to create GPM. All the code below uses gpm
package, that can be loaded using:
import("gpm")
The available strategies depends on the spatial representation of the data. They are summarized in the table below.
From \ To | Points | Lines | Polygons |
---|---|---|---|
Points | Euclidean Distance | Euclidean Distance | Euclidean Distance |
Lines | Euclidean Distance | Euclidean Distance | Euclidean Distance |
Polygons | Euclidean Distance | Euclidean Distance | Intersection Area, Euclidean Distance |
The first example of GPM starts with a strategy that uses the intersection area to create relations
between cells and polygons. A cell is connected to the polygon that has
the largest intersection area. We can create a GPM
using cells
as origin
, farms
as destination
, and area
as strategy
to create relations based on the intersection area.
gpm = GPM{
origin = cells,
strategy = "area",
destination = farms
}
This GPM can be saved as a .gal
file by using save()
, presented
in more details in last section. This neighborhood can then be loaded into a simulation using CellularSpace:loadNeighborhood()
.
gpm:save("cell-neighborhood.gpm")
GPM
can also be used to create new attributes for the CellularSpace
using fill()
. For example, if we want to count how many polygons cover each cell, we can use strategy = "count"
. In this case, we will also use an optional argument max = 5
to indicate that, if a given cell has more than five neighbors, it will use five as the output value.
gpm:fill{
strategy = "count",
attribute = "quantity",
max = 5
}
We can then create a Map
to visualize the output.
map = Map{
target = gpm.origin,
select = "quantity",
min = 0,
max = 5,
slices = 6,
color = "Reds"
}
gpm:fill{
strategy = "maximum",
attribute = "max",
copy = {farm = "id"}
}
-- to paint them with different colores, we use the rest of division by 9
forEachCell(gpm.origin, function(cell)
cell.farm = tonumber(cell.farm) % 9
end)
map = Map{
target = gpm.origin,
select = "farm",
min = 0,
max = 8,
slices = 9,
color = "Set1"
}
The second strategy uses the centroids to create relations between points that are closer than 1000m. To accomplish that, we use \code{getNeighborsMaxDistanceFunction()} to generate a function that returns the neighbors within a given distance. Finally we use \code{createGPM()} from the layer of polygons to generate the results shown in Figure~\ref{fig:1000}.
gpm = GPM{
origin = cells,
destination = communities,
strategy = "distance"
}
gpm:fill{
strategy = "minimum",
attribute = "dist",
copy = "LOCALIDADE"
}
map1 = Map{
target = cells,
select = "dist",
slices = 8,
min = 0,
max = 7000,
color = "YlOrRd",
invert = true
}
map2 = Map{
target = cells,
select = "LOCALIDADE",
value = {"Palhauzinho", "Santa Rosa", "Garrafao", "Mojui dos Campos"},
color = "Set1"
}
gpm:fill{
strategy = "all",
attribute = "d"
}
for i = 0, 3 do
Map{
target = cells,
select = "d_"..i,
slices = 8,
min = 0,
max = 10000,
color = "YlOrRd",
invert = true
}
end
gpm = GPM{
origin = cells,
destination = communities,
distance = 4000
}
gpm:fill{
strategy = "count",
attribute = "quantity"
}
gpm:fill{
strategy = "minimum",
attribute = "dist",
dummy = 7000,
copy = "LOCALIDADE"
}
-- as there is a limit of 4000m, those cells that are far
-- from this distance will not have attribute LOCALIDADE
forEachCell(cells, function(cell)
if not cell.LOCALIDADE then
cell.LOCALIDADE = "<none>"
end
end)
map1 = Map{
target = cells,
select = "quantity",
min = 0,
max = 5,
slices = 6,
color = "RdPu"
}
map2 = Map{
target = cells,
select = "dist",
slices = 8,
min = 0,
max = 7000,
color = "YlOrRd",
invert = true
}
map3 = Map{
target = cells,
select = "LOCALIDADE",
value = {"Palhauzinho", "Santa Rosa", "Garrafao", "Mojui dos Campos", "<none>"},
color = "Set1"
}
The strategy presented in this section computes neighborhoods based on the intersection between
lines and cells. Each cell is connected with the line segments that intersects it. The strategy
"length"
can be used to generate the function that returns the neighbor
lines that intersects a given cell. It gets a layer of cells and a layer of lines as arguments
and returns a function used to effectively create the GPM. Finally, the GPM is effectively created
by the \code{createGPM()}, which receives as arguments the layer of cells passed as argument in the
\code{getNeighborsIntersectionLines()} fuction, and the result returned by it. The code below creates
a neighborhood between the layer "cells" and the layer "rodovias".
gpm = GPM{
origin = cells,
strategy = "length",
destination = roads
}
gpm:fill{
strategy = "count",
attribute = "quantity",
max = 1
}
map = Map{
target = cells,
select = "quantity",
value = {0, 1},
label = {"0", "1 or more"},
color = {"gray", "blue"}
}
In this section, we present a function that computes neighborhoods between a layer of cells and a layer of points based on the "contains" spatial relation. Thus, a cell is connected to the points located inside its area. The function \code{getNeighborsContainedPoints} generates the function that returns the neighbor points located inside the area of a given cell. It gets a layer of cells and a layer of points as arguments and returns a function used to effectively compute the GPM. Finally, the GPM is effectively created by the \code{createGPM()} method, which receives as arguments the layer of cells passed as argument in the \code{getNeighborsContainedPoints()} function, and the result returned by it. The code below creates a neighborhood between the layer "cells" and the layer "comunidades".
gpm = GPM{
origin = cells,
strategy = "contains",
destination = communities
}
gpm:fill{
strategy = "count",
attribute = "quantity"
}
map = Map{
target = cells,
select = "quantity",
value = {0, 1},
color = {"lightGray", "blue"}
}
In this section, we present an strategy to compute neighborhoods between a layer of lines and a layer of polygons, in which each line has as neighbors the polygons intersected by it. The function \code{connectLineToIntersectionPolygons()} generates the function that returns the neighbor polygons intersected by a given line. It gets a layer of lines and a layer of polygons as arguments and returns a function used to effectively compute the GPM. Finally, the GPM is effectively created by the \code{createGPM()} method, which receives as arguments the layer of lines passed as argument in the \code{connectLineToIntersectionPolygons()} function, and the result returned by it. The code below creates a neighborhood between the layer "rodovias" and the layer "lotes".
local states = CellularSpace{
file = filePath("partofbrazil.shp", "gpm"),
geometry = true
}
local gpm = GPM{
origin = states,
strategy = "border",
progress = false
}
This example uses the neighborhood relations directly.
forEachOrderedElement(gpm.neighbor, function(idx, neigh)
print(states:get(idx).name)
forEachOrderedElement(neigh, function(midx, weight)
print("\t"..states:get(midx).name.." ("..string.format("%.2f", weight)..")")
end)
end)
This script will produce the following output:
MINAS GERAIS
RIO DE JANEIRO (0.10)
SAO PAULO (0.25)
ESPIRITO SANTO (0.12)
PARANA
SAO PAULO (0.32)
RIO DE JANEIRO
MINAS GERAIS (0.29)
SAO PAULO (0.14)
ESPIRITO SANTO (0.09)
SAO PAULO
MINAS GERAIS (0.38)
PARANA (0.26)
RIO DE JANEIRO (0.07)
ESPIRITO SANTO
MINAS GERAIS (0.48)
RIO DE JANEIRO (0.11)
The last strategy presented in this vignette computes neighborhoods based on the distance through a given network represented by a set of lines. The original data has to be very well represented, with the starting and ending points of two lines being connected to one another when they share the same position in space. In this type of network, it is possible to enter and leave the roads in any position. \code{createOpenNetwork()} is then used to generate the network. It takes as arguments the destination (reference) points, the lines that will be used to represent the network, and a function that computes the distance on the network given the length of the lines and their id. The code below creates a network that reduces the distance within the network by one fifth of the Euclidean distance for paved roads and by half on the others. The attribute \emph{pavimentada} of the table connected to the layer of lines indicates whether the road is paved or not.
network = Network{
target = communities,
lines = roads,
weight = function(distance, cell)
if cell.STATUS == "paved" then
return distance / 5
else
return distance / 2
end
end,
outside = function(distance) return distance * 4 end
}
gpm = GPM{
network = network,
origin = cells
}
gpm:fill{
strategy = "minimum",
attribute = "dist",
copy = "LOCALIDADE"
}
map1 = Map{
target = cells,
select = "dist",
slices = 10,
min = 0,
max = 14000,
color = "YlOrBr"
}
map2 = Map{
target = cells,
select = "LOCALIDADE",
value = {"Palhauzinho", "Santa Rosa", "Garrafao", "Mojui dos Campos"},
color = "Set1"
}
Figure~\ref{fig:network} shows the polygons drawn with the color of the closest point through the network. There is a current known limitation in the current version of \code{createOpenNetwork()}, that does not work properly when the entry point on the network for a given point is the start or end of a line segment.
ALSO LOAD THE FILE USING LOADNEIGHBORHOOD
Once we have created the GPM through one of the strategies presented above, we can save it in a file, which can be a GPM file (".gpm"),
a GAL file (".gal" or ".GAL") or a GWT file (".gal" or ".GWT"), through the function save()
. The only argument of this function is
the file name to be saved.
The structure of the GPM file is presented in Table \ref{GPMFile}. The first line is the header, and the GPM starts in the second line. In the header, we have the following fields:
\begin{itemize} \item \textbf{Num_attributes} is the number of attributes of the relations. In the GPM, each relation can have several attributes, which represent its properties.
\item \textbf{Layer_1} is the name of the layer for which the GPM was created.
\item \textbf{Layer_2} is the name of the layer where the objects of \textit{Layer_1} has neighbors. If the GPM was created between cells of the same layer, then the name of \textit{Layer_1} is repeated in this field, i. e., \textit{Layer_2} = \textit{Layer_1}.
\item \textbf{Attribute_1, ..., Attribute_N} are the names of the GPM attributes. \end{itemize}
From the second line until the end of the file, the GPM is represented. The neighborhood of each object is represented in two lines. The first contains:
\begin{itemize} \item \textbf{ID_Object_N} is the unique identifier of the N-th object;
\item \textbf{Num_Neighbors} is the number of neighbors of the N-th object; \end{itemize}
and the second contais the neighborhood of the object which ID is in the previous line, represented by the fields below, following the structure presented in Table \ref{GPMFile}:
\begin{itemize} \item \textbf{ID_Neighbor_M} is the M-th neighbor of the N-th object;
\item \textbf{Attrib_K_Neigh_M} is the value of the k-th attribute of the M-th neighbor; \end{itemize}
The structure of the GAL file is presented in Table \ref{GALFile}. It does not store informations about the attributes of the GPM, but only if two objects are neighbors or not. Furthermore, it does not support neighborhoods between objects of different layers. The first line of the file, as well as in the GPM file, is the header, and the GPM starts in the second line. In the header, we have the following fields:
\begin{itemize} \item \textbf{0} is not describe by the creators of the format (Detailed description can be found in the \href{www.unc.edu/~emch/gisph/geoda093.pdf}{GeoDa User's Guide}). Thus, we save it as 0, and it is not used;
\item \textbf{Num_elements} is the number of objects of the \textit{Layer};
\item \textbf{Layer} is the name of the layer for which the GPM was created;
\item \textbf{Key_Variable} is the name of the object attribute used as identifier of the objects. The default value is "object_id_", which is the unique identifier of the objects in TerraLib. \end{itemize}
From the second line until the end of file, the relations are represented. The neighborhood of each object is represented in two lines. The first contains:
\begin{itemize} \item \textbf{ID_Object_N} is the unique identifier of the N-th object;
\item \textbf{Num_Neighbors} is the number of neighbors of the N-th object; \end{itemize}
and the second line contains the unique identifier of the neighbors\ (ID_Neighbor_M) of the N-th object. The code below saves the GPM \textit{gpmdistance}, created in section \ref{EDistance}, in the file "gpmdistance.GAL", presented in Figure \ref{fig:gpmdistGAL}.
The structure of the GWT file is presented in Table \ref{GWTFile}. It can store one of the attributes of the GPM, which name is passed as parameter to the \code{saveGPM()} function. This format, even as the GAL file, does not support neighborhoods objects of different layers. The header of the GWT format is the same of the GAL one. However, from the second line until the end of file, it represent one connection by line, where it has the following fields:
\begin{itemize} \item \textbf{ID_Object_N} is the unique identifier of the object N-th object;
\item \textbf{ID_Neighbor_M} is the M-th neighbor of the N-th object;
\item \textbf{Weight_Neighbor_M} is the weight (attribute value) of the relation between the N-th object and the M-th neighbor. \end{itemize}
The code below save the gpm \textit{gpmdistance}, created in section \ref{EDistance} in the file "gpmnetwork.GWT", presented in Figure \ref{fig:gpmdistGWT}.
<<label="Saving gpmdistance in a GWT file">>= saveGPM(gpmdistance, "gpmdistance.GWT", "lotes", attrib="distance") @
More informations about the GAL and GWT formats can be found in the \href{www.unc.edu/~emch/gisph/geoda093.pdf}{GeoDa User's Guide} and in the \href{http://www.biomedware.com/files/documentation/spacestat/data/export/Spatial_Weights_Files.htm}{SpaceStat documentation}.
For more information on GAL format, see http://geodacenter.asu.edu/software/documentation.
\begin{landscape}
\begin{table} \begin{center} \begin{tabular}{|c|c|c|c|c|c|c|} \hline Num_attributes &Layer_1 &Layer_2 &Attribute_1 &Attribute_2 &... &Attribute_N\ \hline ID_Object_1 &Num_Neighbors & & & & & \ \hline ID_Neighbor_1 &Attrib_1_Neigh_1 &Attrib_2_Neigh_1 &... &Attrib_N_Neigh_1 &ID_Neighbor_2 &... \ \hline ID_Object_2 &... & & & & & \ \hline ... & & & & & & \ \hline \end{tabular} \caption{Structure of the GPM format} \label{GPMFile} \end{center} \end{table}
\begin{table} \begin{center} \begin{tabular}{|c|c|c|c|} \hline 0 &Num_elements &Layer &Key_Variable \ \hline ID_Object_1 &Num_Neighbors & & \ \hline ID_Neighbor_1 &ID_Neighbor_2 &... &ID_Neighbor_N\ \hline ... & & & \ \hline \end{tabular} \caption{Structure of the GAL format} \label{GALFile} \end{center} \end{table}
\begin{table} \begin{center} \begin{tabular}{|c|c|c|c|} \hline 0 &Num_elements &Layer &Key_Variable\ \hline ID_Object_1 &ID_Neighbor_1 &Weight_Neighbor_1 & \ \hline ID_Object_1 &ID_Neighbor_2 &Weight_Neighbor_2 & \ \hline ... & & & \ \hline ID_Object_1 &ID_Neighbor_N &Weight_Neighbor_N & \ \hline ID_Object_2 &ID_Neighbor_1 &Weight_Neighbor_1 & \ \hline ... & & & \ \hline \end{tabular} \caption{Structure of the GWT format} \label{GWTFile} \end{center} \end{table}
If you have comments, doubts or suggestions related to this document, please write a feedback to pedro.andrade <at> inpe.br.
Back to wiki or terrame.org.