惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
Recent Commits to openclaw:main
Recent Commits to openclaw:main
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
罗磊的独立博客
V
Visual Studio Blog
爱范儿
爱范儿
H
Help Net Security
J
Java Code Geeks
I
InfoQ
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recorded Future
Recorded Future
Jina AI
Jina AI
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
GbyAI
GbyAI
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
Scott Helme
Scott Helme
S
SegmentFault 最新的问题
S
Securelist
L
LINUX DO - 热门话题
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
G
Google Developers Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
博客园_首页
B
Blog
F
Fortinet All Blogs
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Forbes - Security
Forbes - Security
S
Security @ Cisco Blogs
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Schneier on Security
Project Zero
Project Zero
Martin Fowler
Martin Fowler
C
Cybersecurity and Infrastructure Security Agency CISA
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
GeoJSON处理
我才是银古 · 2026-06-22 · via 博客园 - 我才是银古

GeoJSON处理

概述

GeoJSON是基于JSON的地理数据格式,具有良好的可读性和跨平台兼容性,是Web GIS开发中最常用的数据交换格式。

GeoJSON结构

基本结构

GeoJSON由Geometry(几何)和Feature(要素)两个核心概念组成:

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [116.397, 39.908]
  },
  "properties": {
    "name": "天安门",
    "type": "landmark"
  }
}

几何类型

GeoJSON支持的几何类型与WKT类似:

// Point
{"type": "Point", "coordinates": [116.397, 39.908]}

// LineString
{"type": "LineString", "coordinates": [[0, 0], [10, 10], [20, 0]]}

// Polygon(外环逆时针,内环顺时针)
{"type": "Polygon", "coordinates": [
  [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]],
  [[2, 2], [8, 2], [8, 8], [2, 8], [2, 2]]
]}

// MultiPoint
{"type": "MultiPoint", "coordinates": [[0, 0], [10, 10]]}

// MultiLineString
{"type": "MultiLineString", "coordinates": [
  [[0, 0], [10, 10]],
  [[20, 20], [30, 30]]
]}

// MultiPolygon
{"type": "MultiPolygon", "coordinates": [
  [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]],
  [[[20, 20], [30, 20], [30, 30], [20, 30], [20, 20]]]
]}

FeatureCollection

多个要素组成的集合:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {...},
      "properties": {...}
    },
    {
      "type": "Feature",
      "geometry": {...},
      "properties": {...}
    }
  ]
}

格式转换

WKT与GeoJSON互转

/**
 * WKT转GEOJSON
 */
public static String wkt2Geojson(String wkt) {
    Geometry geometry = wkt2Geometry(wkt);
    return geometry2Geojson(geometry);
}

/**
 * Geometry转GEOJSON
 */
public static String geometry2Geojson(Geometry geometry) {
    GeometryJSON gjson = new GeometryJSON(16);  // 16位小数精度
    StringWriter writer = new StringWriter();
    gjson.write(geometry, writer);
    return writer.toString();
}

/**
 * GEOJSON转WKT
 */
public static String geojson2Wkt(String geojson) {
    Geometry geometry = geojson2Geometry(geojson);
    return geometry2Wkt(geometry);
}

/**
 * GEOJSON转Geometry
 */
public static Geometry geojson2Geometry(String geojson) {
    GeometryJSON gjson = new GeometryJSON(16);
    return gjson.read(new StringReader(geojson));
}

GeoJSON与EsriJSON互转

/**
 * GEOJSON转ESRIJSON
 */
public static String geoJson2EsriJson(int wkid, String geojson) {
    com.esri.core.geometry.Geometry geometry = 
        EsriUtil.createGeometryByGeoJson(geojson);
    return EsriUtil.getEsriJson(wkid, geometry);
}

/**
 * ESRIJSON转GEOJSON
 */
public static String esriJson2GeoJson(String esrijson) {
    com.esri.core.geometry.Geometry geometry = 
        EsriUtil.createGeometryByJson(esrijson);
    return EsriUtil.getGeoJson(geometry);
}

读取GeoJSON文件

使用GeoTools读取

/**
 * GEOJSON文件转换为WktLayer
 */
public static WktLayer fromGeoJSON(String geojsonPath, GisEngineType gisEngineType) {
    gisEngineType = GisEngineType.getGisEngineType(gisEngineType);
    
    if (gisEngineType == GisEngineType.GEOTOOLS) {
        File file = new File(geojsonPath);
        Charset encoding = EncodingUtil.getFileEncoding(file);
        String geojsonString = FileUtil.readString(file, encoding);
        
        GeometryJSON gjson = new GeometryJSON(16);
        FeatureJSON fjson = new FeatureJSON(gjson);
        
        // 读取Schema
        SimpleFeatureType simpleFeatureType = 
            fjson.readFeatureCollectionSchema(geojsonString, true);
        
        // 读取要素
        ListFeatureCollection featureCollection = 
            new ListFeatureCollection(simpleFeatureType);
        try (FeatureIterator<SimpleFeature> features = 
                fjson.streamFeatureCollection(geojsonString)) {
            while (features.hasNext()) {
                featureCollection.add(features.next());
            }
        }
        
        return fromSimpleFeatureCollection(featureCollection);
    }
    
    return null;
}

使用GDAL读取

/**
 * 使用GDAL读取GeoJSON
 */
public static WktLayer fromGeoJSONWithGDAL(String geojsonPath) {
    String layerName = FileUtil.mainName(geojsonPath);
    return OgrUtil.layer2WktLayer(DataFormatType.GEOJSON, geojsonPath, 
        layerName, null, null);
}

写入GeoJSON文件

使用GeoTools写入

/**
 * WktLayer转换为GEOJSON文件
 */
public static void toGeoJSON(WktLayer wktLayer, String geojsonPath, 
        GisEngineType gisEngineType) {
    wktLayer.check();
    EsriUtil.excludeSpecialFields(wktLayer.getFields());
    
    gisEngineType = GisEngineType.getGisEngineType(gisEngineType);
    
    if (gisEngineType == GisEngineType.GEOTOOLS) {
        SimpleFeatureCollection featureCollection = 
            toSimpleFeatureCollection(wktLayer);
            
        GeometryJSON gjson = new GeometryJSON(16);
        FeatureJSON fjson = new FeatureJSON(gjson);
        
        // 设置坐标系
        CoordinateReferenceSystem crs = 
            featureCollection.getSchema().getCoordinateReferenceSystem();
        if (crs != null) {
            Integer wkid = CrsUtil.standardizeCRS(crs).getKey();
            featureCollection = new ForceCoordinateSystemFeatureResults(
                featureCollection, CrsUtil.getSupportedCRS(wkid).getValue(), false);
        }
        
        String geojsonString = fjson.toString(featureCollection);
        FileUtil.writeString(geojsonString, geojsonPath, "utf-8");
    }
}

使用GDAL写入

/**
 * 使用GDAL写入GeoJSON
 */
public static void toGeoJSONWithGDAL(WktLayer wktLayer, String geojsonPath) {
    String layerName = FileUtil.mainName(geojsonPath);
    OgrUtil.wktLayer2Layer(DataFormatType.GEOJSON, geojsonPath, 
        wktLayer, layerName, null);
}

数据模型转换

WktLayer转SimpleFeatureCollection

/**
 * WktLayer转换为GeoTools SimpleFeatureCollection
 */
public static SimpleFeatureCollection toSimpleFeatureCollection(WktLayer wktLayer) {
    // 创建要素类型
    SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
    tb.setCRS(CrsUtil.getSupportedCRS(wktLayer.getWkid()).getValue());
    tb.setName(wktLayer.getYwName());
    tb.setDefaultGeometry("shape");
    tb.add("shape", wktLayer.getGeometryType().getTypeClass());
    
    // 处理字段(去重并设置类型)
    LinkedHashMap<String, FieldDataType> fieldMap = new LinkedHashMap<>();
    wktLayer.getFields().forEach(ff -> {
        if (!fieldMap.containsKey(ff.getYwName())) {
            fieldMap.put(ff.getYwName(), ff.getDataType());
        } else {
            if (fieldMap.get(ff.getYwName()) == null && ff.getDataType() != null) {
                fieldMap.put(ff.getYwName(), ff.getDataType());
            }
        }
    });
    
    // 添加字段定义
    fieldMap.forEach((k, v) -> {
        if (v != null) {
            tb.add(k, v.getTypeClass());
        } else {
            tb.add(k, String.class);
        }
    });
    
    SimpleFeatureType featureType = tb.buildFeatureType();
    
    // 创建要素集合
    ListFeatureCollection featureCollection = new ListFeatureCollection(featureType);
    for (WktFeature f : wktLayer.getFeatures()) {
        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
        
        // 设置几何
        if (CharSequenceUtil.isNotBlank(f.getWkt())) {
            builder.set("shape", GeometryConverter.wkt2Geometry(f.getWkt()));
        } else {
            builder.set("shape", null);
        }
        
        // 设置属性
        wktLayer.getFields().forEach(ff -> {
            Optional<WktFieldValue> optional = f.getFieldValues().stream()
                .filter(m -> CharSequenceUtil.equals(
                    m.getField().getYwName(), ff.getYwName(), true))
                .findFirst();
            optional.ifPresent(wktFieldValue -> 
                builder.set(wktFieldValue.getField().getYwName(), 
                    wktFieldValue.getValue()));
        });
        
        featureCollection.add(builder.buildFeature(null));
    }
    
    return featureCollection;
}

SimpleFeatureCollection转WktLayer

/**
 * GeoTools SimpleFeatureCollection转换为WktLayer
 */
public static WktLayer fromSimpleFeatureCollection(
        SimpleFeatureCollection featureCollection) {
    WktLayer wktLayer = new WktLayer();
    SimpleFeatureType featureType = featureCollection.getSchema();
    
    // 图层名称
    wktLayer.setYwName(featureType.getName().getLocalPart());
    
    // 坐标系
    CoordinateReferenceSystem crs = featureType.getCoordinateReferenceSystem();
    Map.Entry<Integer, CoordinateReferenceSystem> entry = CrsUtil.standardizeCRS(crs);
    wktLayer.setWkid(entry.getKey());
    wktLayer.setTolerance(CrsUtil.getTolerance(entry.getValue()));
    
    // 几何类型
    Class<?> binding = featureType.getGeometryDescriptor().getType().getBinding();
    if (binding != null) {
        wktLayer.setGeometryType(GeometryType.valueOfByTypeClass(binding));
    }
    
    // 字段定义
    List<WktField> wktFields = new ArrayList<>();
    for (int i = 0; i < featureType.getAttributeCount(); i++) {
        if (featureType.getDescriptor(i) instanceof GeometryDescriptor) {
            continue;
        }
        WktField wktField = new WktField();
        wktField.setYwName(featureType.getDescriptor(i).getLocalName());
        wktField.setZwName(featureType.getDescriptor(i).getLocalName());
        wktField.setDataType(FieldDataType.fieldDataTypeByTypeClass(
            featureType.getDescriptor(i).getType().getBinding()));
        wktFields.add(wktField);
    }
    wktLayer.setFields(wktFields);
    
    // 读取要素
    List<WktFeature> wktFeatures = new ArrayList<>();
    try (FeatureIterator<SimpleFeature> features = featureCollection.features()) {
        while (features.hasNext()) {
            SimpleFeature feature = features.next();
            WktFeature wktFeature = new WktFeature();
            
            // ID
            String id = feature.getID();
            if (CharSequenceUtil.isBlank(id)) {
                id = IdUtil.simpleUUID();
            }
            wktFeature.setWfId(id);
            
            // 几何
            String wkt = ((Geometry) feature.getDefaultGeometry()).toText();
            wktFeature.setWkt(ESRIGeometryUtil.simplify(wkt, wktLayer.getWkid()));
            
            // 属性
            List<WktFieldValue> wktFieldValues = new ArrayList<>();
            for (int i = 0; i < feature.getAttributeCount(); i++) {
                String fn = featureType.getDescriptor(i).getLocalName();
                WktField wktField = wktFields.stream()
                    .filter(f -> CharSequenceUtil.equals(f.getYwName(), fn, true))
                    .findFirst().orElse(null);
                if (wktField != null) {
                    WktFieldValue wktFieldValue = new WktFieldValue();
                    wktFieldValue.setField(wktField);
                    wktFieldValue.setValue(feature.getAttribute(fn));
                    wktFieldValues.add(wktFieldValue);
                }
            }
            wktFeature.setFieldValues(wktFieldValues);
            wktFeatures.add(wktFeature);
        }
    }
    
    wktLayer.setFeatures(wktFeatures);
    wktLayer.check();
    return wktLayer;
}

实践案例

案例1:Shapefile转GeoJSON

常见的格式转换需求:

/**
 * Shapefile转GeoJSON
 */
public void shp2GeoJson(String shpPath, String geojsonPath) {
    // 读取Shapefile
    WktLayer layer = WktLayerConverter.fromShapefile(
        shpPath, null, null, GisEngineType.GEOTOOLS);
    
    // 转换坐标系为WGS84(Web地图常用)
    // CGCS2000(4490)与WGS84差异极小,一般可以直接用
    
    // 写入GeoJSON
    WktLayerConverter.toGeoJSON(layer, geojsonPath, GisEngineType.GEOTOOLS);
}

案例2:合并多个GeoJSON

/**
 * 合并多个GeoJSON文件
 */
public void mergeGeoJson(List<String> inputPaths, String outputPath) {
    List<WktFeature> allFeatures = new ArrayList<>();
    WktLayer template = null;
    
    for (String inputPath : inputPaths) {
        WktLayer layer = WktLayerConverter.fromGeoJSON(inputPath, GisEngineType.GEOTOOLS);
        
        if (template == null) {
            template = layer;
        }
        
        allFeatures.addAll(layer.getFeatures());
    }
    
    if (template != null) {
        template.setFeatures(allFeatures);
        WktLayerConverter.toGeoJSON(template, outputPath, GisEngineType.GEOTOOLS);
    }
}

案例3:GeoJSON简化(减小文件体积)

/**
 * 简化GeoJSON(减小文件体积)
 */
public void simplifyGeoJson(String inputPath, String outputPath, double tolerance) {
    WktLayer layer = WktLayerConverter.fromGeoJSON(inputPath, GisEngineType.GEOTOOLS);
    
    for (WktFeature feature : layer.getFeatures()) {
        Geometry geom = GeometryConverter.wkt2Geometry(feature.getWkt());
        Geometry simplified = JTSGeometryUtil.simplify(geom, tolerance);
        feature.setWkt(simplified.toText());
    }
    
    WktLayerConverter.toGeoJSON(layer, outputPath, GisEngineType.GEOTOOLS);
}

案例4:GeoJSON字段筛选

只保留需要的字段,减小文件体积:

/**
 * 筛选GeoJSON字段
 */
public void filterGeoJsonFields(String inputPath, String outputPath, 
        List<String> keepFields) {
    WktLayer layer = WktLayerConverter.fromGeoJSON(inputPath, GisEngineType.GEOTOOLS);
    
    // 筛选字段定义
    List<WktField> filteredFields = layer.getFields().stream()
        .filter(f -> keepFields.contains(f.getYwName()))
        .collect(Collectors.toList());
    layer.setFields(filteredFields);
    
    // 筛选要素属性
    for (WktFeature feature : layer.getFeatures()) {
        List<WktFieldValue> filteredValues = feature.getFieldValues().stream()
            .filter(v -> keepFields.contains(v.getField().getYwName()))
            .collect(Collectors.toList());
        feature.setFieldValues(filteredValues);
    }
    
    WktLayerConverter.toGeoJSON(layer, outputPath, GisEngineType.GEOTOOLS);
}

案例5:坐标精度控制

控制输出坐标的精度,减小文件体积:

/**
 * 控制坐标精度
 */
public void reduceCoordinatePrecision(String inputPath, String outputPath, 
        int decimalPlaces) {
    WktLayer layer = WktLayerConverter.fromGeoJSON(inputPath, GisEngineType.GEOTOOLS);
    
    // 使用PrecisionModel控制精度
    double scale = Math.pow(10, decimalPlaces);
    PrecisionModel pm = new PrecisionModel(scale);
    GeometryFactory factory = new GeometryFactory(pm);
    
    for (WktFeature feature : layer.getFeatures()) {
        Geometry geom = GeometryConverter.wkt2Geometry(feature.getWkt());
        
        // 重新创建几何,应用新的精度模型
        Geometry reducedGeom = factory.createGeometry(geom);
        GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm);
        reducedGeom = reducer.reduce(geom);
        
        feature.setWkt(reducedGeom.toText());
    }
    
    WktLayerConverter.toGeoJSON(layer, outputPath, GisEngineType.GEOTOOLS);
}

案例6:GeoJSON与前端交互

生成适合前端展示的GeoJSON:

/**
 * 生成Web展示用的GeoJSON
 */
public String prepareForWeb(WktLayer layer) {
    // 1. 确保使用WGS84坐标系
    if (layer.getWkid() != 4490 && layer.getWkid() != 4326) {
        layer = CrsUtil.reproject(layer, 4490);
    }
    
    // 2. 简化几何(根据显示比例尺确定容差)
    double tolerance = 0.0001;  // 约11米
    for (WktFeature feature : layer.getFeatures()) {
        Geometry geom = GeometryConverter.wkt2Geometry(feature.getWkt());
        if (geom.getNumPoints() > 100) {
            Geometry simplified = JTSGeometryUtil.simplify(geom, tolerance);
            feature.setWkt(simplified.toText());
        }
    }
    
    // 3. 转换为GeoJSON
    SimpleFeatureCollection fc = WktLayerConverter.toSimpleFeatureCollection(layer);
    GeometryJSON gjson = new GeometryJSON(6);  // 6位小数足够Web显示
    FeatureJSON fjson = new FeatureJSON(gjson);
    
    return fjson.toString(fc);
}

GeoJSON的优缺点

优点

  1. 可读性好:JSON格式,易于阅读和调试
  2. 跨平台:几乎所有编程语言都支持JSON
  3. Web友好:与JavaScript天然契合
  4. 无字段限制:字段名长度无限制
  5. 单文件:一个文件包含所有信息

缺点

  1. 文件体积大:文本格式比二进制格式大
  2. 解析性能:大数据量时解析较慢
  3. 精度问题:浮点数精度可能有损失
  4. 无空间索引:查询效率不如数据库

使用建议

  • 适合场景:Web展示、数据交换、小规模数据
  • 不适合场景:大数据量存储、高性能查询

小结

本章介绍了GeoJSON处理的核心内容:

  1. 数据结构:Geometry、Feature、FeatureCollection
  2. 格式转换:WKT、GeoJSON、EsriJSON互转
  3. 读写操作:使用GeoTools和GDAL读写GeoJSON文件
  4. 数据模型:WktLayer与SimpleFeatureCollection互转
  5. 优化技巧:简化、字段筛选、精度控制

下一章将介绍PostGIS数据库的交互操作。