在数字化转型与智慧城市建设加速推进的背景下,地理空间数据已成为支撑城市治理、商业决策、社会服务和学术研究的重要基础资源。其中,兴趣面(Area of Interest, AOI) 作为对现实世界中具有明确边界和功能属性的空间单元(如住宅小区、学校、产业园区、商场等)的数字化表达,因其兼具几何形态与语义信息,被广泛应用于人口估算、设施可达性分析、用地分类、热力模拟、应急响应等多种场景。
国内主流地图服务商中,高德地图在其部分 POI(Point of Interest)详情页中,通过内部接口返回了包含 AOI 边界坐标(mining_shape) 的结构化 JSON 数据。这些边界通常由实地测绘或权威数据源生成,精度较高,且覆盖了大量具备实际管理意义的封闭区域(如"浦秀苑""上海师范大学附属闵行第三小学南校"等)。然而,这些数据并未以开放格式直接提供,而是嵌入在网页请求响应中,且格式为非标准的多对象拼接 JSON,无法被 GIS 软件直接读取或批量处理。
针对这一现状,本文提出并实现了一套轻量级、低门槛、可复现的 AOI 数据提取与转换流程:用户仅需通过浏览器开发者工具手动复制目标 POI 的原始响应内容,保存为文本文件后,运行一段基于 Python 的处理脚本,即可自动完成 JSON 对象分割、边界坐标解析、有效多边形构建、属性字段提取、基于 poiid 的智能去重,并最终输出符合 OGC 标准的 Shapefile 文件(采用 EPSG:4326 坐标系),完全兼容 QGIS、ArcGIS 等主流地理信息系统。该方案无需调用高德官方 API,亦不依赖复杂爬虫,有效规避了配额限制与反爬机制,特别适用于小规模、高精度 AOI 数据的定向采集,如社区微更新调研、教育资源分布评估或房地产地块分析等场景。需要强调的是,所获取的数据仍受高德地图相关使用条款约束,仅建议用于个人学习、科研或非商业用途;同时,用户应留意高德所采用的 GCJ-02 坐标系与国际通用 WGS84 之间的系统性偏移,在可视化的过程中需要进行坐标坐标转换。
第一步: 我们先登录高德地图的官方:高德地图,在地图点击某个地点名称,我们就阔以看到对应的轮廓;

**第二步:**我们打开开发者工具(F12 或 Ctrl + Shift + I),找到响应,再找到detail?id这个响应请求,里面的"shape"就是点坐标集;

**第三步:**再往下划,就是具体标签信息;

**第四步:**我们Ctrl+A进行全选,这里新建一个文本文档,把内容粘贴进去即可,然后继续重复此操作;

**第五步:**手动复制完成这些aoi面之后,就是保存一下txt,命名的话可以根据喜好来,然后修改一下txt文件路径,运行一下下面的python脚本即可;
方法思路
- 1.打开 AOI 详情页,通过开发者模式找到对应完整 JSON 响应;
- 2.新建文本文件,命名为:aoi原始数据.txt,复制整个 JSON 内容贴进去(多个重复此操作);
- 3.通过python里面对应gis包功能实现点集成面,并把每个aoi合并成一个shp;
- 4.这里增加了一个坐标转换功能,由高德的GCJ-02坐标系 转 通用的WGS84坐标系;
- 5.结果另存为"aoi数据集"文件夹下的all_aois.shp图层;
完整代码#运行环境 Python 3.11
python
# -*- coding: utf-8 -*-
import json
import geopandas as gpd
from shapely.geometry import Polygon
import os
import re
import math
# ========== GCJ-02 转 WGS84 坐标转换函数 ==========
x_pi = 3.14159265358979324 * 3000.0 / 180.0
pi = 3.1415926535897932384626 # π
a = 6378245.0 # 长半轴
ee = 0.00669342162296594323 # 偏心率平方
def gcj02_to_wgs84(lng, lat):
"""
将GCJ02坐标系(火星坐标系)转换为WGS84坐标系
:param lng: 火星坐标系的经度
:param lat: 火星坐标系的纬度
:return: WGS84坐标系的经纬度列表 [lng, lat]
"""
if out_of_china(lng, lat):
return [lng, lat]
dlat = _transformlat(lng - 105.0, lat - 35.0)
dlng = _transformlng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * pi
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
mglat = lat + dlat
mglng = lng + dlng
return [lng * 2 - mglng, lat * 2 - mglat]
def _transformlat(lng, lat):
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + \
0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))
ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
math.sin(2.0 * lng * pi)) * 2.0 / 3.0
ret += (20.0 * math.sin(lat * pi) + 40.0 *
math.sin(lat / 3.0 * pi)) * 2.0 / 3.0
ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 *
math.sin(lat * pi / 30.0)) * 2.0 / 3.0
return ret
def _transformlng(lng, lat):
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))
ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
math.sin(2.0 * lng * pi)) * 2.0 / 3.0
ret += (20.0 * math.sin(lng * pi) + 40.0 *
math.sin(lng / 3.0 * pi)) * 2.0 / 3.0
ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 *
math.sin(lng / 30.0 * pi)) * 2.0 / 3.0
return ret
def out_of_china(lng, lat):
return not (lng > 73.66 and lng < 135.05 and lat > 3.86 and lat < 53.55)
# ========== 主程序部分==========
INPUT_FILE = "aoi原始数据集.txt"
OUTPUT_FOLDER = "aoi数据集"
MERGED_SHP = "all_aois.shp"
def extract_json_objects(text):
"""从拼接的多个 JSON 对象中提取每个完整对象"""
objects = []
depth = 0
start = None
for i, char in enumerate(text):
if char == '{':
if depth == 0:
start = i
depth += 1
elif char == '}':
depth -= 1
if depth == 0 and start is not None:
objects.append(text[start:i+1])
return objects
def get_unique_id(base):
"""以 poiid 为主键去重,缺失时用 name+address 哈希"""
poiid = base.get("poiid")
if poiid:
return f"ID_{poiid}"
name = base.get("name", "").strip()
addr = base.get("address", "").strip()
if name or addr:
return f"NAMEADDR_{hash(name + '|' + addr)}"
return None
def parse_poi(data_str):
try:
data = json.loads(data_str)
if data.get("status") != "1":
return None
base = data["data"]["base"]
spec = data["data"]["spec"]
# 提取属性
attrs = {
"name": base.get("name", ""),
"tag": base.get("tag", ""),
"address": base.get("address", ""),
"area": float(base.get("geodata", {}).get("aoi", [{}])[0].get("area", 0)),
"poiid": base.get("poiid", ""),
"city_adcode": base.get("city_adcode", ""),
"code": base.get("code", ""),
"classify": base.get("classify", ""),
}
# 解析原始 GCJ-02 坐标字符串
shape_str = spec["mining_shape"]["shape"]
coords_gcj02 = [
p.strip() for p in shape_str.split(";") if p.strip()
]
if len(coords_gcj02) < 3:
return None
# 转换为 WGS84 坐标
wgs84_coords = []
for coord in coords_gcj02:
lng, lat = map(float, coord.split(","))
wgs_lng, wgs_lat = gcj02_to_wgs84(lng, lat)
wgs84_coords.append((wgs_lng, wgs_lat))
polygon = Polygon(wgs84_coords)
if not polygon.is_valid:
polygon = polygon.buffer(0)
if not polygon.is_valid:
return None
return attrs, polygon, base # 返回 base 用于去重
except Exception:
return None
def main():
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
# 读取文件
try:
with open(INPUT_FILE, "r", encoding="utf-8") as f:
content = f.read()
except Exception as e:
print(f"读取文件失败: {e}")
return
# 分割 JSON 对象
json_objects = extract_json_objects(content)
if not json_objects:
print("未找到任何有效的 JSON 对象")
return
seen_ids = set()
features = [] # 存储所有有效 (attrs, geometry)
total = len(json_objects)
kept = 0
print(f"共检测到 {total} 个 POI,开始去重与解析...")
for i, obj_str in enumerate(json_objects, 1):
result = parse_poi(obj_str)
if not result:
continue
attrs, polygon, base = result
unique_id = get_unique_id(base)
if not unique_id or unique_id in seen_ids:
continue
seen_ids.add(unique_id)
features.append((attrs, polygon))
kept += 1
# 合并导出
if features:
# 构建 GeoDataFrame 列表
gdfs = [
gpd.GeoDataFrame([attrs], geometry=[geom], crs="EPSG:4326")
for attrs, geom in features
]
merged = gpd.pd.concat(gdfs, ignore_index=True)
output_path = os.path.join(OUTPUT_FOLDER, MERGED_SHP)
merged.to_file(output_path, encoding="utf-8")
print(f"\n成功导出合并文件: {output_path}")
print(f"共去重后保留 {kept} 个 AOI")
else:
print("\n未生成任何有效 AOI")
if __name__ == "__main__":
main()
这里选取了上海陆家嘴金融中心附近的一些aoi建筑数据做演示,因为脚本运行结束,我们直接得到是就是shp图层,所以我们直接用arcgis进行展示即可;

我们也可以试着把建筑进行拉伸,从立体的视角进行观测,先新建一个局部场景,把shp图层导进去,然后点击要素图层,找到类型选择基本高度,然后选择拉伸字段,这里也可以选择透明度,光照效果一些参数,可以自行调整;

然后就能得到一个3D立体的建筑模型;

文章仅用于分享个人学习成果与个人存档之用,分享知识,如有侵权,请联系作者进行删除。所有信息均基于作者的个人理解和经验,不代表任何官方立场或权威解读。