Chapter 7. 构建应用程序

Table of Contents
7.1. 使用MapServer
7.1.1. 基本用法
7.1.2. 常见问题解答
7.1.3. 高级用法
7.1.4. 示例
7.2. Java客户端(JDBC)
7.3. C客户端(Libpq)
7.3.1. 文本光标
7.3.2. 二进制游标

7.1. 使用MapServer

明尼苏达州地图服务器是符合OpenGIS网络地图服务规范的互联网网络地图服务器。

7.1.1. 基本用法

要在MapServer中使用PostGIS,您需要了解如何配置MapServer,这不在本文档的讨论范围之内。本节介绍特定的PostGIS问题和配置详细信息。

要将PostGIS与MapServer配合使用,您将需要:

  • PostGIS的0.6版或更高版本。

  • MapServer 3.5版或更高版本。

MapServer像访问任何其他PostgreSQL客户端一样,使用 libpq 界面。这意味着MapServer可以安装在任何可以通过网络访问PostGIS服务器的计算机上,并使用PostGIS作为数据源。系统之间的连接越快越好。

  1. 使用您想要的任何选项编译和安装MapServer,包括“--with-postgis”配置选项。

  2. 在MapServer Mapfile 中,添加一个PostGIS图层。例如:

    LAYER
      CONNECTIONTYPE postgis
      NAME "widehighways"
      # Connect to a remote spatial database
      CONNECTION "user=dbuser dbname=gisdatabase host=bigserver"
      PROCESSING "CLOSE_CONNECTION=DEFER"
      # Get the lines from the 'geom' column of the 'roads' table
      DATA "geom from roads using srid=4326 using unique gid"
      STATUS ON
      TYPE LINE
      # Of the lines in the extents, only render the wide highways
      FILTER "type = 'highway' and numlanes >= 4"
      CLASS
        # Make the superhighways brighter and 2 pixels wide
        EXPRESSION ([numlanes] >= 6)
        STYLE
          COLOR 255 22 22
          WIDTH 2
        END
      END
      CLASS
        # All the rest are darker and only 1 pixel wide
        EXPRESSION ([numlanes] < 6)
        STYLE
          COLOR 205 92 82
        END
      END
    END

    在上面的示例中,特定于PostGIS的指令如下:

    CONNECTIONTYPE

    对于PostGIS图层,此名称始终为“postgis”。

    CONNECTION

    数据库连接由a‘连接字符串’控制,这是一组标准的键和值,如下所示(在 < > ):

    用户= <username> 密码= <password> 数据库名= <username> 主机名= <server> 端口= <5432>

    空的连接字符串仍然有效,任何键/值对都可以省略。至少,您通常需要提供要连接的数据库名称和用户名。

    DATA

    此参数的形式为“ <geocolumn> 从… <tablename> 使用SRID= <srid> 使用唯一 <primary key> “其中,列是要呈现到地图的空间列,SRID是列使用的SRID,主键是表主键(或任何其他具有索引的唯一值列)。

    您可以省略“USING SRID”和“USING UNIQUE”子句,如果可能,MapServer将自动确定正确的值,但代价是为每个地图绘制在服务器上运行几个额外的查询。

    PROCESSING

    如果有多个层,则输入CLOSE_CONNECTION=DEFER将重用现有连接,而不是关闭它们。这提高了速度。请参阅以了解 MapServer PostGIS性能提示 以获得更详细的解释。

    FILTER

    筛选器必须是有效的SQL字符串,对应于SQL查询中通常跟随在“WHERE”关键字之后的逻辑。因此,例如,要仅渲染具有6条或更多车道的道路,请使用过滤器“num_lanes > =6“。

  3. 在空间数据库中,确保已为要绘制的任何图层构建了空间(GIST)索引。

    CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometrycolumn] );
  4. 如果要使用MapServer查询图层,还需要在DATA语句中使用“USING UNIQUE”子句。

    在执行查询时,MapServer需要每个空间记录的唯一标识符,而MapServer的PostGIS模块使用您指定的唯一值来提供这些唯一标识符。使用表主键是最佳实践。

7.1.2. 常见问题解答

7.1.2.1. 当我使用 EXPRESSION 在我的映射文件中,条件永远不会返回为真,即使我知道值存在于我的表中。
7.1.2.2. 我对shapefile使用的过滤器不适用于相同数据的PostGIS表。
7.1.2.3. 我的PostGIS层绘制速度比Shapefile层慢得多,这正常吗?
7.1.2.4. 我的PostGIS层绘制得很好,但查询速度非常慢。怎么啦?
7.1.2.5. 我是否可以使用“地理”列(在PostGIS 1.5中新增)作为MapServer图层的源?

7.1.2.1.

当我使用 EXPRESSION 在我的映射文件中,条件永远不会返回为真,即使我知道值存在于我的表中。

与Shape文件不同,在表达式中必须使用引用PostGIS字段名称 小写

EXPRESSION ([numlanes] >= 6)

7.1.2.2.

我对shapefile使用的过滤器不适用于相同数据的PostGIS表。

与形状文件不同,PostGIS图层的过滤器使用SQL语法(它们被附加到PostGIS连接器为MapServer中的图形图层生成的SQL语句中)。

FILTER "type = 'highway' and numlanes >= 4"

7.1.2.3.

我的PostGIS层绘制速度比Shapefile层慢得多,这正常吗?

通常,在给定地图中绘制的要素越多,PostGIS的速度就越有可能比shapefile慢。对于要素相对较少的地图(100个),PostGIS通常会更快。对于要素密度较高(1000s)的地图,PostGIS总是速度较慢。

如果您发现了严重的绘制性能问题,则可能是您尚未在表上构建空间索引。

postgis# CREATE INDEX geotable_gix ON geotable USING GIST ( geocolumn );
postgis# VACUUM ANALYZE;

7.1.2.4.

我的PostGIS层绘制得很好,但查询速度非常慢。怎么啦?

为了提高查询速度,您的空间表必须有一个唯一键,并且必须有该唯一键的索引。

您可以指定地图服务器要使用的唯一密钥 USING UNIQUE 从句在您的 DATA 行:

DATA "geom FROM geotable USING UNIQUE gid"

7.1.2.5.

我是否可以使用“地理”列(在PostGIS 1.5中新增)作为MapServer图层的源?

是!MapServer将地理列理解为与几何列相同,但始终使用SRID 4326。只需确保在您的 DATA 陈述。其他一切与几何体的工作原理完全相同。

DATA "geog FROM geogtable USING SRID=4326 USING UNIQUE gid"

7.1.3. 高级用法

这个 USING 伪SQL子句用于添加一些信息,以帮助地图服务器了解更复杂查询的结果。更具体地说,当视图或子选择用作源表时( DATA 定义)对于mapserver来说,自动确定每一行的唯一标识符以及表的SRID更加困难。这个 USING 子句可以为映射服务器提供以下两条信息:

DATA "geom FROM (
  SELECT
    table1.geom AS geom,
    table1.gid AS gid,
    table2.data AS data
  FROM table1
  LEFT JOIN table2
  ON table1.id = table2.id
) AS new_table USING UNIQUE gid USING SRID=4326"
使用唯一 <uniqueid>

在执行地图查询时,MapServer要求每行具有唯一的ID,以便标识该行。通常,它从系统表中标识主键。但是,视图和子选择不会自动具有已知的唯一列。如果要使用MapServer的查询功能,则需要确保您的视图或子选择包含唯一值列,并使用 USING UNIQUE 。例如,您可以为此显式选择表的主键值中的一个,或者选择保证对结果集唯一的任何其他列。

[Note]

“查询地图”是点击地图以查询该位置的地图要素信息的操作。不要将“映射查询”与 DATA 定义。

使用SRID= <srid>

PostGIS需要知道几何图形正在使用哪个空间参考系统,以便将正确的数据返回给MapServer。通常,可以在PostGIS数据库的“GEOMETRY_COLUMNS”表中找到此信息,但是,对于在即创建的表(如SUBSELECT和VIEW),这是不可能的。因此, USING SRID= 选项允许在 DATA 定义。

7.1.4. 示例

让我们从一个简单的例子开始,然后逐步向上。请考虑以下MapServer图层定义:

LAYER
  CONNECTIONTYPE postgis
  NAME "roads"
  CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
  DATA "geom from roads"
  STATUS ON
  TYPE LINE
  CLASS
    STYLE
      COLOR 0 0 0
    END
  END
END

此图层将以黑线显示Roads表中的所有道路几何图形。

现在,假设我们只想显示高速公路,直到我们放大到至少1:100000的比例--下两个层将实现此效果:

LAYER
  CONNECTIONTYPE postgis
  CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
  PROCESSING "CLOSE_CONNECTION=DEFER"
  DATA "geom from roads"
  MINSCALE 100000
  STATUS ON
  TYPE LINE
  FILTER "road_type = 'highway'"
  CLASS
    COLOR 0 0 0
  END
END
LAYER
  CONNECTIONTYPE postgis
  CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
  PROCESSING "CLOSE_CONNECTION=DEFER"
  DATA "geom from roads"
  MAXSCALE 100000
  STATUS ON
  TYPE LINE
  CLASSITEM road_type
  CLASS
    EXPRESSION "highway"
    STYLE
      WIDTH 2
      COLOR 255 0 0
    END
  END
  CLASS
    STYLE
      COLOR 0 0 0
    END
  END
END

第一个层在比例大于1:100000时使用,并且仅以黑线显示“高速公路”类型的道路。这个 FILTER 选项会导致仅显示“高速公路”类型的道路。

第二层在比例小于1:100000时使用,并将高速公路显示为双粗红线,其他道路显示为常规黑线。

因此,我们只使用MapServer功能做了几件有趣的事情,但我们的 DATA SQL语句保持简单。假设道路的名称存储在另一个表中(无论出于何种原因),我们需要做一个连接来获取它并标记我们的道路。

LAYER
  CONNECTIONTYPE postgis
  CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
  DATA "geom FROM (SELECT roads.gid AS gid, roads.geom AS geom,
        road_names.name as name FROM roads LEFT JOIN road_names ON
        roads.road_name_id = road_names.road_name_id)
        AS named_roads USING UNIQUE gid USING SRID=4326"
  MAXSCALE 20000
  STATUS ON
  TYPE ANNOTATION
  LABELITEM name
  CLASS
    LABEL
      ANGLE auto
      SIZE 8
      COLOR 0 192 0
      TYPE truetype
      FONT arial
    END
  END
END

当比例尺降至1:20000或更小时,此注释图层会为所有道路添加绿色标签。它还演示了如何在 DATA 定义。

7.2. Java客户端(JDBC)

Java客户端可以直接以文本表示形式或使用与PostGIS捆绑在一起的JDBC扩展对象来访问PostgreSQL数据库中的PostGIS“几何”对象。为了使用扩展对象,“postgis.jar”文件必须与“postgresql.jar”JDBC驱动程序包一起位于您的CLASSPATH中。

import java.sql.*;
import java.util.*;
import java.lang.*;
import org.postgis.*;

public class JavaGIS {

public static void main(String[] args) {

  java.sql.Connection conn;

  try {
    /*
    * Load the JDBC driver and establish a connection.
    */
    Class.forName("org.postgresql.Driver");
    String url = "jdbc:postgresql://localhost:5432/database";
    conn = DriverManager.getConnection(url, "postgres", "");
    /*
    * Add the geometry types to the connection. Note that you
    * must cast the connection to the pgsql-specific connection
    * implementation before calling the addDataType() method.
    */
    ((org.postgresql.PGConnection)conn).addDataType("geometry",Class.forName("org.postgis.PGgeometry"));
    ((org.postgresql.PGConnection)conn).addDataType("box3d",Class.forName("org.postgis.PGbox3d"));
    /*
    * Create a statement and execute a select query.
    */
    Statement s = conn.createStatement();
    ResultSet r = s.executeQuery("select geom,id from geomtable");
    while( r.next() ) {
      /*
      * Retrieve the geometry as an object then cast it to the geometry type.
      * Print things out.
      */
      PGgeometry geom = (PGgeometry)r.getObject(1);
      int id = r.getInt(2);
      System.out.println("Row " + id + ":");
      System.out.println(geom.toString());
    }
    s.close();
    conn.close();
  }
catch( Exception e ) {
  e.printStackTrace();
  }
}
}

根据类型:点、线串、多边形、多点、多线串、多面,包含特定的拓扑几何对象(抽象类的子类)的包装对象。

PGgeometry geom = (PGgeometry)r.getObject(1);
if( geom.getType() == Geometry.POLYGON ) {
  Polygon pl = (Polygon)geom.getGeometry();
  for( int r = 0; r < pl.numRings(); r++) {
    LinearRing rng = pl.getRing(r);
    System.out.println("Ring: " + r);
    for( int p = 0; p < rng.numPoints(); p++ ) {
      Point pt = rng.getPoint(p);
      System.out.println("Point: " + p);
      System.out.println(pt.toString());
    }
  }
}

扩展对象的JavaDoc为几何对象中的各种数据访问器函数提供了参考。

7.3. C客户端(Libpq)

...

7.3.1. 文本光标

...

7.3.2. 二进制游标

...