[postgis-users] Postigs layer in Mapserver
Jan Hartmann
jhart at frw.uva.nl
Fri Nov 15 08:47:29 PST 2002
(Just an addition to Chris's and Horst's replies, with a few ramblings
of my own)
Oscar Testa wrote:
> DATA "the_geom from (select locarg.the_geom, locarg.oid, count(*) as
> cantidad from locarg, lugar where locarg.codpostal = ugar.codigopostal
> group by 1,2) as prueba"
You can't combine a COUNT() statement with a geometry. Count summarizes
the fields described by GROUP BY. For example, if you have a table with
has population data containing rows for many people with the same postal
code , you can do:
select count (*) from data
group by postal_code
and you wil get back something like
count
------
123
432
77786
445
...
...
This gives the number of people for each postal code.
You cannot just add the postal_code field to this query, because there
are many persons for every postal code. If you want to see the postal
code for each total, you have to use an aggregate function for that too,
like min:
select min(postal_code), count(*) from data
group by postal_code
which will give back:
min | count
---------------
1011 123
1012 432
1014 77786
1015 445
... ...
... ...
For a better layout, rename the aggregate column names:
select min(postal_code) as postal_code, count(*) as total_pop from data
group by postal_code:
postal_code | total_pop
--------------------
1011 123
... ...
And now the big leap: how to plot this aggregated table with
MapServer/PostGIS. Just as with the postal code, you cannot just put in
a geometry field in the population table, because there are many
different rows with data for the same location. Instead you use a second
table (let's call it zipcodes), in which there are two field: the postal
code and the geometry (its central coordinate in most cases). You can
link this table to the computed aggregate table like this:
select the_geom from
zipcodes,
(select min(postal_code) as aggr_postal_code, count(*) as total_pop
from data
group by postal_code
) as aggrdata
where zipcodes.postal_code = aggrdata.postal_code
The thing to remember here is that ALL the computed variables of the
inner select AND the inner select itself have to be named explicitly (as
aggr_postal_code, as total_pop, as aggrdata). That way MapServer can see
them.
A MapServer CLASS statement using this DATA could be
LABELITEM aggr_postal_code
CLASS
EXPRESSION "([total_pop] < 1000)"
COLOR 30 50 60
SYMBOL ...
SIZE ...
END
CLASS
EXPRESSION "([total_pop] < 2000)"
COLOR ...
SYMBOL ...
SIZE ...
END
...
etc
This wil return a map with symbols at the postal code locations, sized
accorded to the number of people on that location.
This is pretty hefty SQL, but it is worth some hard thinking to figure
out how it works. Marketing analysis with GIS is done almost exclusively
this way. The beauty about PostGIS is the close integration between
database queries and mapping. It's very easy (by scripting the DATA
statement) to get all kinds of related spatial distributions this way
(partial populations, male/female distributions, customer types, just
take your choice). Of course, this can be done by desktop applications
like ArcView or MapInfo, but with considerably more effort and not as
elegant or generalized, and not for WebMapping. You need at least
ArcIMS/Oracle Spatial for that, and I don't think they will do it as
elegantly as MapServer/PostGIS (or as fast, if you manage your indexes
well).
Jan Hartmann
Department of Geography
University of Amsterdam
jhart at frw.uva.nl
More information about the postgis-users
mailing list