需求:
1秒内完成5w+条标注数据渲染到地图上,点击每个标注点显示该点的详情信息。
问题:
加载上万标注点到地图上性能特别差,天地图提供的组件MarkerClusterer,CloudMarkerCollection无法满足需求。
解决:
使用geoserver发布WMS地图服务+空间搜索获取标注点的基础信息
流程:
1、构造基础数据的shapfile文件
2、将shpfile文件通过geoserver发布成WMS服务
3、编写GeoServer的SLD文件 给发布的图层添加自定义图标
4、将WMS服务叠加到天地图地图上
5、监听用户图层点击事件,构造请求参数调用geosever获取基础信息。
结果:
注:图标的大小根据实际情况在SLD文件中设置
实现:
一、构造基础数据的shapfile文件+将shpfile文件通过geoserver发布成WMS服务
1)geosever核心依赖
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<properties>
<geotools.version>25.1</geotools.version>
</properties>
<dependency>
<groupId>it.geosolutions</groupId>
<artifactId>geoserver-manager</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-process</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geotiff</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-process-raster</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-process-feature</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
2)发布shapfile到geoserver核心代码
public void autoPublishLayer() {
//图层名称
String layerName = "layer-" + layer.getId();
try {
//图层数据列表,包含经纬度坐标和基础信息
List<LayerData> layerDatas = findLayerData(layer);
GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326);
SimpleFeatureType TYPE = DataUtilities.createType(layerName,
"the_geom:Point:srid=4326,name:String,address:String,id:String,layerId:String");
List<SimpleFeature> features = new ArrayList<>();
for (LayerData layerData : layerDatas) {
double longitude = layerData.getLon();
double latitude = layerData.getLat();
Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
//设置图层基础信息
features.add(SimpleFeatureBuilder.build(TYPE, new Object[]{point, layerData.getName(), layerData.getAddress(),
layerData.getId(), layerData.getLayerId()}, layerData.getId()));
}
SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
//构造文件路径
String path = SysFileUtil.getBaseDir(Global.getConfig("apps.file.upload")) + File.separator + "xf-layer" + File.separator + layerName + File.separator;
File filePath = new File(path);
if (!filePath.exists()) {
filePath.mkdirs();
}
File shpFile = new File(path + layerName + ".shp");
if (!shpFile.exists()) {
shpFile.createNewFile();
}
//生成shapFile
writeToShapeFile(shpFile, collection, TYPE);
File zipFile = new File(path + layerName + ".zip");
if (!zipFile.exists()) {
zipFile.createNewFile();
}
//geoserver发布需要压缩成ZIP
ZipFileUtil.compressFiles2Zip(filePath.listFiles((dir, name) -> !name.endsWith("zip")), zipFile.getAbsolutePath());
log.info("图层{}shp文件已生成。", layerName);
//发布压缩后的文件
if (weatherWarningService.publishShp(zipFile, "sc119", layerName, "sc119:" + layer.getIcon())) {
log.info("图层{}服务已发布。", layerName);
}
} catch (Exception e) {
log.error("图层{}发布失败!,错误信息:{}", layerName, e.getMessage());
}
}
public void writeToShapeFile(File shapeFile, SimpleFeatureCollection collection, SimpleFeatureType CITY) throws Exception {
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<>();
ShapefileDataStore dataStore = setDataStoreParams(dataStoreFactory, params, shapeFile, CITY);
dataStore.setCharset(StandardCharsets.UTF_8);
// If you decide to use the TYPE type and create a Data Store with it,
// You will need to uncomment this line to set the Coordinate Reference System
// newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);
Transaction transaction = new DefaultTransaction("create");
String typeName = dataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
if (featureSource instanceof SimpleFeatureStore) {
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
try {
featureStore.addFeatures(collection);
transaction.commit();
} catch (Exception problem) {
problem.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
}
}
}
3)发布shapfile到geoserver后可以登录到geoserver管理页面进行图层预览。
二、编写GeoServer的SLD文件 给发布的图层添加自定义图标
1) 给图层设置单个图标的SLD文件
图标存放路径为geoserver工作空间的指定工作区下,按实际路径修 xlink:href="images/szd.png"即可。
<?xml version="1.0" encoding="UTF-8"?>
<sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld"
xmlns:sld="http://www.opengis.net/sld"
xmlns:gml="http://www.opengis.net/gml"
xmlns:ogc="http://www.opengis.net/ogc"
version="1.0.0">
<sld:UserLayer>
<sld:LayerFeatureConstraints>
<sld:FeatureTypeConstraint/>
</sld:LayerFeatureConstraints>
<sld:UserStyle>
<sld:Name>xfs</sld:Name>
<sld:Title>xfs</sld:Title>
<sld:Abstract/>
<sld:FeatureTypeStyle>
<sld:Name>default</sld:Name>
<sld:Title/>
<sld:Abstract/>
<sld:FeatureTypeName>Feature</sld:FeatureTypeName>
<sld:SemanticTypeIdentifier>generic:geometry</sld:SemanticTypeIdentifier>
<sld:SemanticTypeIdentifier>simple</sld:SemanticTypeIdentifier>
<sld:Rule>
<sld:Name>default</sld:Name>
<sld:Title/>
<sld:Abstract/>
<sld:PointSymbolizer>
<sld:Graphic>
<ExternalGraphic>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="images/szd.png"/>
<Format>image/png</Format>
</ExternalGraphic>
<sld:Mark>
<sld:WellKnownName>circle</sld:WellKnownName>
<sld:Stroke/>
</sld:Mark>
<sld:Size>25.000000px</sld:Size>
<sld:Rotation>0.0</sld:Rotation>
</sld:Graphic>
</sld:PointSymbolizer>
<sld:TextSymbolizer/>
</sld:Rule>
</sld:FeatureTypeStyle>
</sld:UserStyle>
</sld:UserLayer>
</sld:StyledLayerDescriptor>
2) 给图层设置多个图标的SLD文件
原理:根据图层某个基础属性字段的值做为判断依据,设置图标路径。
<?xml version="1.0" encoding="ISO-8859-1"?>
<StyledLayerDescriptor version="1.0.0"
xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NamedLayer>
<Name>hidden-danger</Name>
<UserStyle>
<Title>A red line style</Title>
<FeatureTypeStyle>
<Rule>
<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>属性名称</ogc:PropertyName>
<ogc:Literal>1</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>
<PointSymbolizer>
<Graphic>
<ExternalGraphic>
<OnlineResource xlink:type="simple" xlink:href="images/xfs.png" />
<Format>image/png</Format>
</ExternalGraphic>
<Size><ogc:Literal>16</ogc:Literal></Size>
</Graphic>
</PointSymbolizer>
</Rule>
<Rule>
<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>属性名称</ogc:PropertyName>
<ogc:Literal>3</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>
<PointSymbolizer>
<Graphic>
<ExternalGraphic>
<OnlineResource xlink:type="simple" xlink:href="images/szd.png" />
<Format>image/png</Format>
</ExternalGraphic>
<Size><ogc:Literal>16</ogc:Literal></Size>
</Graphic>
</PointSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
三、使用vue将WMS服务叠加到天地图地图上
addWmsLayer(item) {
if (item.wmsLayer) {
item.wmsLayer.setOpacity(1)
return
}
var config = {
version: "1.1.1", //请求服务的版本
layers: item.layer,
transparent: true, //输出图像背景是否透明
styles: "", //每个请求图层的用","分隔的描述样式
format: "image/png", //输出图像的类型
zIndex: 100
};
var wmsLayer = new T.TileLayer.WMS(item.url, config);
item.wmsLayer = wmsLayer
this.Map.addLayer(wmsLayer);
}
四、监听用户图层点击事件,调用geosever获取基础信息。
1)监听图层点击事件
this.Map.addEventListener("click", function (e) {
//调用处理函数
that.clickMmsLayer(e)
});
2)调用geoserver的GetFeatureInfo请求获取基础数据
核心思路:
构造geoserver获取基础数据需要的几个请求参数:经纬度坐标、图层地址和名称、bbox参数。
bbox参数由于天地图API不支持,需要动态调整bbox的大小。
clickMmsLayer(e) {
let arr = []
var lat = e.lnglat.lat //点击事件返回的经纬度坐标
var lon = e.lnglat.lng
var zoom = this.Map.getZoom();
//bbox数据根据不同层级自动调整大小
var step = 0.001
if (zoom < 14) {
step = (70 - 4 * zoom) * 0.01;
}
let item={
url:"http://localhost:8080/geoserver/workspace/wms",
item.layer:"test",
}
//构造访问geoserver的获取基础数据的GetFeatureInfo请求
var url = item.url + "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetFeatureInfo&FORMAT=application/json&TRANSPARENT=true" +
"&QUERY_LAYERS=" + item.layer + "&LAYERS=" + item.layer +
"&exceptions=application/json&SRS=EPSG:4326&INFO_FORMAT=application/json&STYLES=&x=50&y=50&WIDTH=101&HEIGHT=101&BBOX=" + (lon - step).toFixed(6) + "," + (lat - step).toFixed(6) + "," + (lon + step).toFixed(6) + "," + (lat + step).toFixed(6)
axios.get(url).then((res) => {
console.log(res)
}).catch(e => {
console.log(e)
})
}
})
},