java生成mvt切片的方法實現(xiàn)
目錄
好久沒發(fā)博客了,每日或忙碌、或悠閑、或喜或悲、時悵時朗,或許如蘇軾說的“人生如逆旅,我亦是行人”。俱已矣,雖有感于斯,然生活仍要繼續(xù)。今將最近研究的地理圖層服務(wù)器的一種生成矢量切片的方法示予諸位,望諸位勉之!
新的想法
在做地理信息項目的時候,總免不了與各種圖層打交道,尤其是大數(shù)據(jù)量的json圖層,動輒幾十兆的數(shù)據(jù)需要在前后端之間傳輸,有些項目又不可能將隨時可能變動的json數(shù)據(jù)存在前端,最終還是需要后端接口返回。因此,最普遍的方法就是借助第三方的空間服務(wù)器將數(shù)據(jù)發(fā)布成切片形式由前端調(diào)用。但是第三方服務(wù)器往往需要采購,開源的第三方服務(wù)器往往不被支持(國產(chǎn)化),所以我們希望將這部分功能直接由后端實現(xiàn)…
Java能為切片做什么
java生成vector-tile的方法網(wǎng)上有很多,大部分采用的是no.ecc.vectortile包,這是挪威國家地理信息中心發(fā)布的一個支持使用java生成矢量切片的jar。我們也打算采用這種成熟可靠的開源組件(畢竟國產(chǎn)化并沒有限制使用第三方j(luò)ar)。

引入依賴
我們先引入需要的依賴,目前能用的vectortile包只有1.2.5可用,其他版本不兼容創(chuàng)建出來的Geometry對象。
<!-- geo包含的依賴 --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.22.2</version> </dependency> <dependency> <groupId>com.google.protobuf.nano</groupId> <artifactId>protobuf-javanano</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>no.ecc.vectortile</groupId> <artifactId>java-vector-tile</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>com.vividsolutions</groupId> <artifactId>jts</artifactId> <version>1.13</version> </dependency> <dependency> <groupId>org.osgeo</groupId> <artifactId>proj4j</artifactId> <version>0.1.0</version> </dependency>
如何轉(zhuǎn)換xyz
通過轉(zhuǎn)換函數(shù)將xyz轉(zhuǎn)換為bbox,也就是查詢范圍
public static String xyz2prj4326BBox(int z, int x, int y) {
String bbox = "";
double n = Math.pow(2, z);
double lon_min = (x / n) * 360.0 - 180.0;
double lat_min = 90.0 - (((y + 1) / n) * 360.0);
double lon_max = ((x + 1) / n) * 360.0 - 180.0;
double lat_max = 90.0 - ((y / n) * 360.0);
bbox = lon_min + ","+lat_min+","+lon_max+","+lat_max;
return bbox;
}
public static String parseXyz2Bound(int x,int y,int z){
StringBuilder sb = new StringBuilder("POLYGON ((");
double lngLeft = MercatorProjection.tileXToLongitude(x, (byte)z) - 0.00105;
double latUp = MercatorProjection.tileYToLatitude(y, (byte)z) + 0.00105;
double lngRight = MercatorProjection.tileXToLongitude(x + 1, (byte)z) + 0.00105;
double latDown = MercatorProjection.tileYToLatitude(y + 1, (byte)z) - 0.00105;
sb.append(lngLeft +" "+latUp+", ");
sb.append(lngRight +" "+latUp+", ");
sb.append(lngRight +" "+latDown+", ");
sb.append(lngLeft +" "+latDown+", ");
sb.append(lngLeft +" "+latUp+")) ");
return sb.toString();
}
public static void convert2Piexl(int x, int y, int z, Geometry geom){
double px = MercatorProjection.tileXToPixelX(x);
double py = MercatorProjection.tileYToPixelY(y);
Coordinate[] cs = geom.getCoordinates();
byte zoom = (byte)z;
for(Coordinate c : cs){
c.x = (int)(((MercatorProjection.longitudeToPixelX(c.x, zoom)) - px) * 16);
c.y = (int)(((MercatorProjection.latitudeToPixelY(c.y, zoom)) - py) * 16);
// c.z = 218;
}
}這里我們直接踩前人的肩膀
package com.address.geo.utils;
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* 前人的肩膀
* A static class that implements spherical mercator projection.
*/
public final class MercatorProjection {
private MercatorProjection() {
}
/**
* Convert a longitude coordinate (in degrees) to a horizontal distance in
* meters from the zero meridian.
*
* @param longitude
* in degrees
* @return longitude in meters in spherical mercator projection
*/
public static double longitudeToMetersX(double longitude) {
return WGS84.EQUATORIALRADIUS * Math.toRadians(longitude);
}
/**
* Convert a meter measure to a longitude.
*
* @param x
* in meters
* @return longitude in degrees in spherical mercator projection
*/
public static double metersXToLongitude(double x) {
return Math.toDegrees(x / WGS84.EQUATORIALRADIUS);
}
/**
* Convert a meter measure to a latitude.
*
* @param y
* in meters
* @return latitude in degrees in spherical mercator projection
*/
public static double metersYToLatitude(double y) {
return Math.toDegrees(Math.atan(Math
.sinh(y / WGS84.EQUATORIALRADIUS)));
}
/**
* Convert a latitude coordinate (in degrees) to a vertical distance in
* meters from the equator.
*
* @param latitude
* in degrees
* @return latitude in meters in spherical mercator projection
*/
public static double latitudeToMetersY(double latitude) {
return WGS84.EQUATORIALRADIUS
* Math.log(Math.tan(Math.PI / 4
+ 0.5 * Math.toRadians(latitude)));
}
/**
* Calculate the distance on the ground that is represented by a single
* pixel on the map.
*
* @param latitude
* the latitude coordinate at which the resolution should be
* calculated.
* @param zoom
* the zoom level at which the resolution should be calculated.
* @return the ground resolution at the given latitude and zoom level.
*/
public static double calculateGroundResolution(double latitude, byte zoom) {
return Math.cos(latitude * Math.PI / 180) * 40075016.686
/ ((long) Tile.TILE_SIZE << zoom);
}
/**
* Convert a latitude coordinate (in degrees) to a pixel Y coordinate at a
* certain zoom level.
*
* @param latitude
* the latitude coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the pixel Y coordinate of the latitude value.
*/
public static double latitudeToPixelY(double latitude, byte zoom) {
double sinLatitude = Math.sin(latitude * Math.PI / 180);
return (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude))
/ (4 * Math.PI))
* ((long) Tile.TILE_SIZE << zoom);
}
/**
* Convert a latitude coordinate (in degrees) to a tile Y number at a
* certain zoom level.
*
* @param latitude
* the latitude coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the tile Y number of the latitude value.
*/
public static long latitudeToTileY(double latitude, byte zoom) {
return pixelYToTileY(latitudeToPixelY(latitude, zoom), zoom);
}
/**
* Convert a longitude coordinate (in degrees) to a pixel X coordinate at a
* certain zoom level.
*
* @param longitude
* the longitude coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the pixel X coordinate of the longitude value.
*/
public static double longitudeToPixelX(double longitude, byte zoom) {
return (longitude + 180) / 360 * ((long) Tile.TILE_SIZE << zoom);
}
/**
* Convert a longitude coordinate (in degrees) to the tile X number at a
* certain zoom level.
*
* @param longitude
* the longitude coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the tile X number of the longitude value.
*/
public static long longitudeToTileX(double longitude, byte zoom) {
return pixelXToTileX(longitudeToPixelX(longitude, zoom), zoom);
}
/**
* Convert a pixel X coordinate at a certain zoom level to a longitude
* coordinate.
*
* @param pixelX
* the pixel X coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the longitude value of the pixel X coordinate.
*/
public static double pixelXToLongitude(double pixelX, byte zoom) {
return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoom)) - 0.5);
}
/**
* Convert a pixel X coordinate to the tile X number.
*
* @param pixelX
* the pixel X coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the tile X number.
*/
public static long pixelXToTileX(double pixelX, byte zoom) {
return (long) Math.min(Math.max(pixelX / Tile.TILE_SIZE, 0), Math.pow(
2, zoom) - 1);
}
/**
* Convert a tile X number to a pixel X coordinate.
*
* @param tileX
* the tile X number that should be converted
* @return the pixel X coordinate
*/
public static double tileXToPixelX(long tileX) {
return tileX * Tile.TILE_SIZE;
}
/**
* Convert a tile Y number to a pixel Y coordinate.
*
* @param tileY
* the tile Y number that should be converted
* @return the pixel Y coordinate
*/
public static double tileYToPixelY(long tileY) {
return tileY * Tile.TILE_SIZE;
}
/**
* Convert a pixel Y coordinate at a certain zoom level to a latitude
* coordinate.
*
* @param pixelY
* the pixel Y coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the latitude value of the pixel Y coordinate.
*/
public static double pixelYToLatitude(double pixelY, byte zoom) {
double y = 0.5 - (pixelY / ((long) Tile.TILE_SIZE << zoom));
return 90 - 360 * Math.atan(Math.exp(-y * 2 * Math.PI)) / Math.PI;
}
/**
* Converts a pixel Y coordinate to the tile Y number.
*
* @param pixelY
* the pixel Y coordinate that should be converted.
* @param zoom
* the zoom level at which the coordinate should be converted.
* @return the tile Y number.
*/
public static long pixelYToTileY(double pixelY, byte zoom) {
return (long) Math.min(Math.max(pixelY / Tile.TILE_SIZE, 0), Math.pow(
2, zoom) - 1);
}
/**
* Convert a tile X number at a certain zoom level to a longitude
* coordinate.
*
* @param tileX
* the tile X number that should be converted.
* @param zoom
* the zoom level at which the number should be converted.
* @return the longitude value of the tile X number.
*/
public static double tileXToLongitude(long tileX, byte zoom) {
return pixelXToLongitude(tileX * Tile.TILE_SIZE, zoom);
}
/**
* Convert a tile Y number at a certain zoom level to a latitude coordinate.
*
* @param tileY
* the tile Y number that should be converted.
* @param zoom
* the zoom level at which the number should be converted.
* @return the latitude value of the tile Y number.
*/
public static double tileYToLatitude(long tileY, byte zoom) {
return pixelYToLatitude(tileY * Tile.TILE_SIZE, zoom);
}
/**
* Computes the amount of latitude degrees for a given distance in pixel at
* a given zoom level.
*
* @param deltaPixel
* the delta in pixel
* @param lat
* the latitude
* @param zoom
* the zoom level
* @return the delta in degrees
*/
public static double deltaLat(double deltaPixel, double lat, byte zoom) {
double pixelY = latitudeToPixelY(lat, zoom);
double lat2 = pixelYToLatitude(pixelY + deltaPixel, zoom);
return Math.abs(lat2 - lat);
}
}數(shù)據(jù)如何查詢
如果是postgres,并安裝了postgis插件,那么只需要一句sql就可以查詢出mvt格式的切片
-- code是我查詢st_r_sn表的條件(諸位自行斟酌適應(yīng)自己的條件)
String sql = "WITH mvtgeom AS (" +
"SELECT ST_AsMVTGeom(T.geometry, ST_TileEnvelope(" + xyz2prj4326BBox(z, x, y)+", 4490 ),4096,0,true) AS geom, " +
"qh_name as name, id from st_r_sn as T where qh_code = '"+code+"')" +
"SELECT ST_AsMVT(mvtgeom.*) as data FROM mvtgeom";如果是達夢這個繼承了oracle衣缽的國產(chǎn)數(shù)據(jù)庫,那就稍微有點麻煩了,需要先安裝dmgeo擴展包,然后用ecc轉(zhuǎn)換,值得注意的是你的空間字段需要指定SRID(坐標(biāo)系)和查詢語句一致,如4490,如果在數(shù)據(jù)入庫時并沒有指定SRID,那么達夢會默認是這個字段的SRID=0,你也可以用setSRID函數(shù)重新賦值
String sql = "select qh_name as name, dmgeo.st_astext(geom) as geom from t_geo " + "where qh_code = '"+code+"' and dmgeo.st_intersects(geom, dmgeo.st_geomfromtext(?, 4490))";
如何輸出mvt格式給前端
如果是postgres或者金倉數(shù)據(jù)庫,直接輸出byte數(shù)組
Map<String, Object> results = jdbc.queryForMap(sql);
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip;
try {
gzip = new GZIPOutputStream(out);
gzip.write((byte[]) results.get("data"));
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();如果是達夢
try {
String tile = parseXyz2Bound(x, y, z);
List<Map<String, Object>> results = jdbc.queryForList(sql, tile);
VectorTileEncoder vte = new VectorTileEncoder(4096, 16, false);
for (Map<String, Object> m : results) {
String wkt = (String) m.get("geom");
Geometry geom = new WKTReader().read(wkt);
convert2Piexl(x, y, z, geom);
m.remove("geom");
Random r = new Random();
long id = r.nextLong();
vte.addFeature("boundary", m, geom, id);
}
if (results.size() > 0) {
return vte.encode();
} else {
System.out.println("區(qū)劃" + code + "的索引x:" + x + ",y:" + y + ",z:" + z + "在范圍" + tile + "未查詢到數(shù)據(jù)");
return null;
}
} catch (com.vividsolutions.jts.io.ParseException e) {
e.printStackTrace();
}
return null;最后在controller上調(diào)用就可以了
@ApiOperation("mvt切片")
@ApiImplicitParams({
@ApiImplicitParam(name = "code", value = "查詢代碼", paramType = "path", dataType = "String", required = true),
@ApiImplicitParam(name = "z", value = "層", paramType = "path", dataType = "int", required = true),
@ApiImplicitParam(name = "x", value = "行", paramType = "path", dataType = "int", required = true),
@ApiImplicitParam(name = "y", value = "列", paramType = "path", dataType = "int", required = true)
})
@RequestMapping(value = "/mvt/[code]/{z}/{x}/{y}", produces="application/x-protobuf", method = RequestMethod.GET)
public byte[] spatial(@PathVariable String code, @PathVariable int x, @PathVariable int y, @PathVariable int z) {
return dao.getMvtTile(code, x, y, z);
}前端如何調(diào)用
<html>
<head>
<meta charset='utf-8'/>
<title data-i18n="resources.title_beijingMVTVectorTile"></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script type="text/javascript" src="jquery.min.js"></script>
<!-- <script type="text/javascript" src="iclient9-mapboxgl.min.js"></script> -->
<script type="text/javascript" src="mapbox-gl.min.js"></script>
<link type="text/css" rel="stylesheet" href="mapbox-gl.min.css" rel="external nofollow" />
<script type="text/javascript" src="mapbox-gl-draw.js"></script>
<link type="text/css" rel="stylesheet" href="mapbox-gl-draw.css" rel="external nofollow" />
<style>
body {
margin: 0;
padding: 0;
}
#draws{
position: absolute;
z-index: 10;
margin-left: 200px;
margin-top: 50px;
}
#draw_area{
position: absolute;
z-index: 10;
margin-left: 100px;
margin-top: 50px;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="draw_area" style="color: chocolate; font-weight: bold;"></div>
<div id="draws"></div>
<div id='map'></div>
<script type="text/javascript">
var host = window.isLocal ? window.server : 'https://iserver.supermap.io';;
//var host = window.isLocal ? window.server : "http://192.168.1.13:8090";
var drawHandleModel;
mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';
var attribution = "<a target='_blank'>? Mapbox </a>" +
" with <span>? <a target='_blank'>SuperMap iClient</a> | </span>" +
" Map Data <span>? <a target='_blank'>SuperMap iServer</a></span> ";
var map = new mapboxgl.Map({
container: 'map', // container id
style: {
"version": 8,
"sources": {
"raster-tiles": {
"attribution": attribution,
"type": "raster",
"tiles": [host + '/iserver/services/map-china400/rest/maps/ChinaDark/zxyTileImage.png?z={z}&x={x}&y={y}'],
"tileSize": 256
}
},
"layers": [{
"id": "simple-tiles",
"type": "raster",
"source": "raster-tiles",
"minzoom": 0,
"maxzoom": 22
}]
},
center: [104.641354,28.758767],
zoom: 7
});
map.addControl(new mapboxgl.NavigationControl(), 'top-left');
map.on('load',function(){
// // 四川省界
// map.addLayer({
// 'id': 'boundary1',
// 'type': 'line',
// 'source': {
// type: 'vector',
// tiles: [
// // 切片服務(wù)地址tagola切片
// `http://127.0.0.1:9494/address/geo/boundary/510000000000/{z}/{x}/{y}`
// ],
// minzoom: 3,
// maxzoom: 16,
// },
// 'source-layer': 'boundary',
// 'paint': {
// 'line-color':'red',
// 'line-width': 5
// }
// });
//成都市
map.addLayer({
'id': 'boundary2',
'type': 'fill',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510100000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'fill-color': 'yellow', // blue color fill
'fill-opacity': 0.5
}
});
// 自貢市
map.addLayer({
'id': 'boundary3',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510300000000/{z}/{x}/{y}`,
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 攀枝花市
map.addLayer({
'id': 'boundary4',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510400000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 瀘州市
map.addLayer({
'id': 'boundary5',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510500000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 德陽市
map.addLayer({
'id': 'boundary6',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510600000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 綿陽市
map.addLayer({
'id': 'boundary7',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510700000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 廣元市
map.addLayer({
'id': 'boundary8',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510800000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 遂寧市
map.addLayer({
'id': 'boundary9',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510900000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 內(nèi)江市
map.addLayer({
'id': 'boundary10',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511000000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 樂山市
map.addLayer({
'id': 'boundary11',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511100000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 南充市
map.addLayer({
'id': 'boundary13',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511300000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 眉山市
map.addLayer({
'id': 'boundary14',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511400000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 宜賓市
map.addLayer({
'id': 'boundary15',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511500000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 廣安市
map.addLayer({
'id': 'boundary16',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511600000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 達州市
map.addLayer({
'id': 'boundary17',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511700000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 雅安市
map.addLayer({
'id': 'boundary18',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511800000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 巴中市
map.addLayer({
'id': 'boundary19',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/511900000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 資陽市
map.addLayer({
'id': 'boundary20',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/512000000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 阿壩
map.addLayer({
'id': 'boundary32',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/513200000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 甘孜
map.addLayer({
'id': 'boundary33',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/513300000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 涼山
map.addLayer({
'id': 'boundary34',
'type': 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/513400000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
'paint': {
'line-color':'#66B852',
'line-width':3.5
}
});
// 省界用動畫效果
map.addLayer({
type: 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510000000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
id: 'line-background',
paint: {
'line-color': 'yellow',
'line-width': 6,
'line-opacity': 0.4
}
});
map.addLayer({
type: 'line',
'source': {
type: 'vector',
tiles: [
// 切片服務(wù)地址tagola切片
`http://127.0.0.1:9494/address/geo/boundary/510000000000/{z}/{x}/{y}`
],
minzoom: 5,
maxzoom: 16,
},
'source-layer': 'boundary',
id: 'line-dashed',
paint: {
'line-color': 'yellow',
'line-width': 6,
'line-dasharray': [0, 4, 3]
}
});
// 線段動畫效果
// technique based on https://jsfiddle.net/2mws8y3q/
// an array of valid line-dasharray values, specifying the lengths of the alternating dashes and gaps that form the dash pattern
const dashArraySequence = [
[0, 4, 3],
[0.5, 4, 2.5],
[1, 4, 2],
[1.5, 4, 1.5],
[2, 4, 1],
[2.5, 4, 0.5],
[3, 4, 0],
[0, 0.5, 3, 3.5],
[0, 1, 3, 3],
[0, 1.5, 3, 2.5],
[0, 2, 3, 2],
[0, 2.5, 3, 1.5],
[0, 3, 3, 1],
[0, 3.5, 3, 0.5]
];
let step = 0;
function animateDashArray(timestamp) {
// Update line-dasharray using the next value in dashArraySequence. The
// divisor in the expression `timestamp / 50` controls the animation speed.
const newStep = parseInt(
(timestamp / 50) % dashArraySequence.length
);
if (newStep !== step) {
map.setPaintProperty(
'line-dashed',
'line-dasharray',
dashArraySequence[step]
);
step = newStep;
}
// Request the next frame of the animation.
requestAnimationFrame(animateDashArray);
}
// start the animation
animateDashArray(0);
})
</script>
</body>
</html>最后由于數(shù)據(jù)保密問題不能發(fā)完整代碼,但是思路是前人走過的,可以放心使用。
到此這篇關(guān)于java生成mvt切片的方法實現(xiàn)的文章就介紹到這了,更多相關(guān)java生成mvt切片內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+jersey+tomcat實現(xiàn)跨域方式上傳文件到服務(wù)器的方式
這篇文章主要介紹了springboot+jersey+tomcat實現(xiàn)跨域方式上傳文件到服務(wù)器,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
Java InheritableThreadLocal用法詳細介紹
InheritableThreadLocal繼承了ThreadLocal,此類擴展了ThreadLocal以提供從父線程到子線程的值的繼承:當(dāng)創(chuàng)建子線程時,子線程接收父線程具有的所有可繼承線程局部變量的初始值。 通常子線程的值與父線程的值是一致的2022-09-09
MyBatisPlus+Lombok實現(xiàn)分頁功能的方法詳解
Lombok是一個Java類庫,提供了一組注解,簡化POJO實體類開發(fā)。本文將為大家介紹一下Lombok的使用以及如何利用MyBatisPlus+Lombok實現(xiàn)分頁功能,感興趣的可以動手嘗試一下2022-07-07

