shapefile文件转geojson并在地图上展示
工作中经常需要预览shp文件,一般都是用arcmap、qgis之类桌面端软件操作。对于本机没有装软件的情况下,很难预览。因此在后台写了一个读取shp的服务,前端通过上传,实现读取shp,并在地图上展示位置和属性。
geotools 读取shp,并转为geojson格式
主要使用了geotools来进行读取数据:
引入依赖,这里使用最新版27-SNAPSHOT
<geotools.version>27-SNAPSHOT</geotools.version>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.7.14</version>
</dependency>
geotools 主要代码
/**
* shp转换为Geojson
* @param file_
* @return Map
* @throws Exception
*/
@PostMapping("/shpToGeojson")
@ResponseBody
public Map<String, String> shpToGeojson(
@RequestParam("shp") MultipartFile file_[]) throws Exception {
Map<String, String> map = new HashMap<String, String>();
//存放shp文件
File[] targetFileForWrite = null;
if (file_ != null && file_.length > 0) {
//判断是否为压缩文件,目前只支持zip
if (file_[0].getOriginalFilename().endsWith(".zip")) {
File file = new File(FileUtil.getTmpDirPath(), "temp.zip");
file_[0].transferTo(file);
//使用hutool的解压功能
File files = ZipUtil.unzip(file);
targetFileForWrite = files.listFiles();
} else {
//创建临时文件路径
String filePath = System.getProperty("java.io.tmpdir");
targetFileForWrite = new File[file_.length];
//获取shp文件
for (int i = 0; i < file_.length; i++) {
String tempName = file_[i].getOriginalFilename();
File tempFile = new File(filePath, tempName);
//获取父目录
File fileParent = tempFile.getParentFile();
if (!fileParent.exists()) {
if (!fileParent.mkdirs()) {
map.put("code", "500");
map.put("status", "failure");
map.put("message", "文件不存在!");
return map;
}
}
// 转存文件
file_[i].transferTo(tempFile);
targetFileForWrite[i] = tempFile;
}
}
}
//至少包含 dbf shp shx 三个文件
if (targetFileForWrite == null || targetFileForWrite.length < 3) {
map.put("code", "500");
map.put("status", "failure");
map.put("message", "请传入完整shp文件!");
return map;
}
//shp 数据源
ShapefileDataStore shpDataStore = null;
try {
// 获取shp数据源,这里用数组中任意一个文件即可读取所有文件
shpDataStore = new ShapefileDataStore(targetFileForWrite[0].toURI().toURL());
// 设置编码
Charset charset = Charset.forName("UTF-8");
shpDataStore.setCharset(charset);
//获取feature对象组
String typeName = shpDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = null;
featureSource = shpDataStore.getFeatureSource(typeName);
//获取图形数组
SimpleFeatureCollection result = featureSource.getFeatures();
//将feature转为geojson
StringWriter writer = new StringWriter();
FeatureJSON json = new FeatureJSON();
json.writeFeatureCollection(result, writer);
map.put("code", "200");
map.put("status", "success");
map.put("message", writer.toString());
} catch (Exception e) {
map.put("code", "500");
map.put("status", "failure");
map.put("message", e.getMessage());
e.printStackTrace();
} finally {
//关闭数据源
if (shpDataStore != null) {
shpDataStore.dispose();
}
//删除临时文件
for (File file : targetFileForWrite) {
if (file != null && file.exists()) {
file.delete();
}
}
}
return map;
}
前端请求服务,并使用openlayers展示数据
前端使用ajax发起请求,获取后台数据,并使用openlayers展示数据。
html 代码
使用form表单实现上传,可以根据项目选择上传方式。
<!--上传 form 和 input -->
<form id="form" method="post" enctype="multipart/form-data">
<input onchange="upload()" multiple="multiple" id="shp" type="file" name="shp"/>
</form>
js代码
为了方便使用openlayers,这里使用ES6方式实现。
引入依赖
// 引入依赖 start =================================================================
import GeoJSON from 'ol/format/GeoJSON';
import Map from 'ol/Map';
import View from 'ol/View';
import {Fill, Stroke} from 'ol/style';
import {OSM, Vector as VectorSource} from 'ol/source';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
// 引入依赖 end =================================================================
//实现方法
let upload = function() {
// 使用formdata获取上传数据
let formData = new FormData($("#form")[0]);
//定位地图加载数据方法
let toMap = function (features) {
// 创建资源对象
const vectorSource = new VectorSource({
features: features,
});
// 创建图层对象
const vectorLayer = new VectorLayer({
source: vectorSource,
//设置样式
style: new Style({
//边框样式
stroke: new Stroke({
color: 'red',
width: 2,
lineDash: [3, 5]
}),
//填充样式
fill: new Fill({
color: 'rgba(0, 0, 255, 0.3)',
}),
}),
});
// 创建地图对象
const map = new Map({
layers: [
// 创建谷歌底图
new TileLayer({
source: new OSM(),
}),
vectorLayer,
],
// 地图容器div的id
target: 'map',
view: new View({
// 定位中心点
center: [0, 0],
// 定位级别
zoom: 2,
}),
});
}
//这里使用ajax请求数据,可根据项目调整。
$.ajax({
url: baseUrl + "/shpToGeojson",
dataType: "json",
type: 'POST',
//使用 formdata 上传文件
data: formData,
cache: false,
contentType: false,
processData: false,
async: true,
success: function (data) {
if (data.status == "success") {
//将json字符串转为json对象
let coordinate = JSON.parse(data.message);
//判断geojson必有属性
if (coordinate.features && coordinate.type) {
//openlayers 读取geojson数据,生成图形对象
let features = (new GeoJSON()).readFeatures(coordinate, {
dataProjection: 'EPSG:4326', // 设定数据使用的坐标系
featureProjection: 'EPSG:3857' // 设定当前地图使用的feature的坐标系
});
//定义绑定事件
let clickFunc = function (e) {
//输出图形对象属性。
console.log(e.target.getProperties())
}
for (let i = 0; i < features.length; i++) {
const feature = features[i];
//绑定点击事件
feature.on('click', clickFunc);
}
//将图形对象添加到地图上
toMap(features)
}
}
}
});
}
示例展示
底图为天地图影像图,数据为北京市各区。左侧展示地图位置,右侧输出属性数据。
这里的属性数据,可以以弹出信息框的方式展示,效果更好,有时间更新。
参考网站:
[1]: openlayers官网读取geojson
[2]: geotools官网API FeatureJSON操作