├── .travis.yml
├── .gitignore
├── src
├── test
│ ├── resources
│ │ ├── mbtiles4j.properties
│ │ └── mbtiles4j
│ │ │ └── mbtiles4j-test.mbtiles
│ └── java
│ │ └── mbtiles4j
│ │ └── MBTilesUtilsTest.java
└── main
│ ├── resources
│ └── mbtiles4j.properties
│ ├── java
│ └── mbtiles4j
│ │ ├── MBTiles4jContextListener.java
│ │ ├── TileServlet.java
│ │ └── MBTilesUtils.java
│ └── webapp
│ └── WEB-INF
│ └── web.xml
├── LICENSE
├── README.md
└── pom.xml
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .classpath
3 | .project
4 | .settings
5 | target/
6 |
--------------------------------------------------------------------------------
/src/test/resources/mbtiles4j.properties:
--------------------------------------------------------------------------------
1 | tile-dbs = mbtiles4j-test
2 |
3 | mbtiles4j-test.path = mbtiles4j-test.mbtiles
4 |
--------------------------------------------------------------------------------
/src/test/resources/mbtiles4j/mbtiles4j-test.mbtiles:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gisarmory/mbtiles4j/HEAD/src/test/resources/mbtiles4j/mbtiles4j-test.mbtiles
--------------------------------------------------------------------------------
/src/main/resources/mbtiles4j.properties:
--------------------------------------------------------------------------------
1 | #tile-dbs = db1,db2,db3
2 | #
3 | #db1.path = /path/to/your/database.mbtiles
4 | #db2.path = /path/to/your/database.mbtiles
5 | #db3.path = /path/to/your/database.mbtiles
6 |
7 | tile-dbs = db1
8 |
9 | db1.path = D:/OSM/chinaAll.mbtiles
--------------------------------------------------------------------------------
/src/main/java/mbtiles4j/MBTiles4jContextListener.java:
--------------------------------------------------------------------------------
1 | package mbtiles4j;
2 |
3 | import javax.servlet.ServletContextEvent;
4 | import javax.servlet.ServletContextListener;
5 |
6 | public class MBTiles4jContextListener implements ServletContextListener {
7 |
8 | @Override
9 | public void contextInitialized(ServletContextEvent sce) {
10 | MBTilesUtils.connect();
11 | }
12 |
13 | @Override
14 | public void contextDestroyed(ServletContextEvent sce) {
15 | MBTilesUtils.disconnect();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | mbtiles4j
7 |
8 | Tile Servlet
9 | mbtiles4j.TileServlet
10 |
11 |
12 | Tile Servlet
13 | /*
14 |
15 |
16 | mbtiles4j.MBTiles4jContextListener
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 GIS兵器库
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | mbtiles4j
2 | =========
3 |
4 | 一个用 java 语言写的 [MBTiles](https://github.com/mapbox/mbtiles-spec) 瓦片发布程序
5 |
6 | 本代码库基于 [mbtiles4j](https://github.com/jtreml/mbtiles4j) 库修改而来。原代码库是针对 png 图片的访问,本代码库改为了针对 pbf 文件的访问。感谢原作者!
7 |
8 |
9 |
10 | 配置
11 | -------------
12 |
13 | 编辑 `src/main/resources` 路径下的 `mbtiles4j.properties` ,配置你要发布的 `.mbtiles` 文件
14 |
15 | ```properties
16 | tile-dbs = db1,db2,db3
17 |
18 | db1.path = D:/files/map1.mbtiles
19 | db2.path = D:/files/map2.mbtiles
20 | db3.path = D:/files/map3.mbtiles
21 | ```
22 |
23 | **别名配置:** tile-dbs = <数据别名>,<数据别名>
24 |
25 | `tile-dbs` 配置数据别名,这个别名用于数据的识别,包括配置 `.mbtiles` 文件的路径,和文件发布后浏览器的访问路径。别名支持配置多个,中间用英文逗号分隔。
26 |
27 | **文件配置格式:** <数据别名>.path=mbtiles 文件绝对路径
28 |
29 | 例如:`db1.path` 中的 `db1` 是 `tile-dbs` 中配置的别名,`D:/files/map1.mbtiles` 是文件的绝对路径。
30 |
31 | 上述配置中,`db1` 内的矢量瓦片在浏览器中的访问地址为:`htp://
:/mbtiles4j/db1/z/x/y.pbf`
32 |
33 |
34 |
35 | 数据格式
36 | -----------------------
37 |
38 | mbtiles4j 发布的 MBTiles 瓦片,遵循 [TMS](http://en.wikipedia.org/wiki/Tile_Map_Service) 瓦片格式规范。
39 |
40 |
41 |
42 | 调用示例 (mapboxGL)
43 | -----------------------
44 |
45 | 这是 [mapboxGL](https://github.com/mapbox/mapbox-gl-js) 调用 mbtiles4j 发布瓦片的示例,关于数据源配置的部分
46 |
47 | ```json
48 | {
49 | "sources": {
50 | "openmaptiles": {
51 | "type": "vector",
52 | "scheme": "tms",
53 | "tiles": ["http://127.0.0.1:7200/mbtiles4j/db1/{z}/{x}/{y}.pbf"],
54 | "minzoom": 0,
55 | "maxzoom": 14
56 | }
57 | }
58 | }
59 | ```
60 |
61 |
62 |
63 | 更多背景
64 | -----------------------
65 |
66 | [http://gisarmory.xyz/blog/index.html?blog=OSMMbtiles](http://gisarmory.xyz/blog/index.html?blog=OSMMbtiles)
67 |
68 |
69 |
70 | 关于
71 | -----------------------
72 |
73 | 欢迎关注微信公众号《GIS兵器库》
--------------------------------------------------------------------------------
/src/test/java/mbtiles4j/MBTilesUtilsTest.java:
--------------------------------------------------------------------------------
1 | package mbtiles4j;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 | import java.util.Map;
9 |
10 | import org.apache.commons.io.IOUtils;
11 | import org.junit.AfterClass;
12 | import org.junit.Assert;
13 | import org.junit.BeforeClass;
14 | import org.junit.Test;
15 |
16 | public class MBTilesUtilsTest {
17 |
18 | private static MBTilesUtils mbtu;
19 |
20 | private static String path;
21 |
22 | @BeforeClass
23 | public static void extractTestDatabase() throws IOException {
24 | Map dbs = MBTilesUtils.getDatabases();
25 | Assert.assertTrue(dbs.size() == 1);
26 |
27 | String db = dbs.keySet().iterator().next();
28 | path = dbs.get(db);
29 |
30 | InputStream is = null;
31 | OutputStream os = null;
32 | try {
33 | is = MBTilesUtilsTest.class.getResourceAsStream(path);
34 | os = new FileOutputStream(path);
35 | IOUtils.copy(is, os);
36 | os.flush();
37 | } finally {
38 | if (os != null) {
39 | os.close();
40 | }
41 | if (is != null) {
42 | is.close();
43 | }
44 | }
45 |
46 | MBTilesUtils.connect();
47 | mbtu = MBTilesUtils.getInstance(db);
48 | }
49 |
50 | @Test
51 | public void retrieveTiles() {
52 | check(0, 0, 0);
53 | checkNot(0, 1, 0);
54 | check(0, 1, 1);
55 | checkNot(0, 0, 3);
56 | }
57 |
58 | @AfterClass
59 | public static void deleteTestDatabase() {
60 | MBTilesUtils.disconnect();
61 | new File(path).delete();
62 | }
63 |
64 | private void check(int x, int y, int z) {
65 | byte[] tile = mbtu.getTiles(x, y, z);
66 | Assert.assertTrue(tile != null && tile.length > 0);
67 | }
68 |
69 | private void checkNot(int x, int y, int z) {
70 | byte[] tile = mbtu.getTiles(x, y, z);
71 | Assert.assertTrue(tile == null);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/mbtiles4j/TileServlet.java:
--------------------------------------------------------------------------------
1 | package mbtiles4j;
2 |
3 | import java.io.IOException;
4 | import java.util.regex.Pattern;
5 |
6 | import javax.servlet.ServletException;
7 | import javax.servlet.ServletOutputStream;
8 | import javax.servlet.http.HttpServlet;
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 |
12 | import org.apache.commons.io.IOUtils;
13 | import org.apache.commons.lang3.StringUtils;
14 |
15 | @SuppressWarnings("serial")
16 | public class TileServlet extends HttpServlet {
17 |
18 | @Override
19 | protected void doGet(HttpServletRequest request,
20 | HttpServletResponse response) throws ServletException, IOException {
21 | String path = request.getPathInfo();
22 | if (StringUtils.isEmpty(path)) {
23 | response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
24 | return;
25 | }
26 |
27 | path = path.substring(1);
28 | String[] split = path.split(Pattern.quote("/"));
29 |
30 | if (split.length != 4) {
31 | response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
32 | return;
33 | }
34 |
35 | // if (!split[3].toLowerCase().endsWith(".png")) {
36 | // response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
37 | // return;
38 | // }
39 |
40 | // split[3] = split[3].replace(".png", "");
41 |
42 |
43 | split[3] = split[3].replace(".pbf", "");
44 |
45 | MBTilesUtils mbtu = MBTilesUtils.getInstance(split[0]);
46 | if (mbtu == null) {
47 | response.setStatus(HttpServletResponse.SC_NOT_FOUND);
48 | return;
49 | }
50 |
51 | int z;
52 | int y;
53 | int x;
54 | try {
55 | z = Integer.parseInt(split[1]);
56 | x = Integer.parseInt(split[2]);
57 | y = Integer.parseInt(split[3]);
58 | } catch (NumberFormatException e) {
59 | response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
60 | return;
61 | }
62 |
63 | byte[] tile = mbtu.getTiles(x, y, z);
64 | if (tile == null) {
65 | response.setStatus(HttpServletResponse.SC_NOT_FOUND);
66 | return;
67 | }
68 |
69 | response.setContentType("application/x-protobuf");
70 | response.setHeader("Content-Encoding", "gzip");
71 | response.setContentLength(tile.length);
72 |
73 | ServletOutputStream oStream = response.getOutputStream();
74 | IOUtils.write(tile, oStream);
75 | oStream.flush();
76 | oStream.close();
77 | }
78 |
79 | @Override
80 | protected void doPost(HttpServletRequest request,
81 | HttpServletResponse response) throws ServletException, IOException {
82 | doGet(request, response);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | mbtiles4j
5 | 0.0.1-SNAPSHOT
6 | 4.0.0
7 | mbtiles4j
8 | war
9 | mbtiles4j
10 |
11 |
12 | 2.2.1
13 |
14 |
15 |
16 | UTF-8
17 |
18 |
19 |
20 |
21 | commons-io
22 | commons-io
23 | 2.4
24 |
25 |
26 | org.apache.commons
27 | commons-lang3
28 | 3.1
29 |
30 |
31 | javax.servlet
32 | javax.servlet-api
33 | 3.1.0
34 | provided
35 |
36 |
37 | org.xerial
38 | sqlite-jdbc
39 | 3.7.2
40 |
41 |
42 | junit
43 | junit
44 | 4.11
45 | test
46 |
47 |
48 |
49 |
50 | mbtiles4j
51 |
52 |
53 | org.apache.maven.plugins
54 | maven-compiler-plugin
55 | 3.1
56 |
57 | 1.6
58 | 1.6
59 |
60 |
61 |
62 | org.apache.maven.plugins
63 | maven-war-plugin
64 | 2.3
65 |
66 | **/client/**
67 |
68 |
69 | true
70 | true
71 |
72 |
73 | src/main/webapp/WEB-INF/web.xml
74 |
75 |
76 | ${basedir}/src/main/webapp/WEB-INF
77 | true
78 | WEB-INF
79 |
80 | web.xml
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.codehaus.mojo
88 | versions-maven-plugin
89 | 2.0
90 |
91 |
92 |
93 |
94 | scm:git:git@github.com:jtreml/mbtiles4j.git
95 | scm:git:git@github.com:jtreml/mbtiles4j.git
96 | https://github.com/jtreml/mbtiles4j
97 |
98 |
99 | GitHub Issues
100 | https://github.com/jtreml/mbtiles4j/issues
101 |
102 |
103 | Jürgen Treml
104 | http://www.juergentreml.de
105 |
106 |
107 | https://travis-ci.org/jtreml/mbtiles4j
108 | Travis CI
109 |
110 |
111 |
--------------------------------------------------------------------------------
/src/main/java/mbtiles4j/MBTilesUtils.java:
--------------------------------------------------------------------------------
1 | package mbtiles4j;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.sql.Connection;
6 | import java.sql.DriverManager;
7 | import java.sql.PreparedStatement;
8 | import java.sql.ResultSet;
9 | import java.sql.SQLException;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 | import java.util.Properties;
13 | import java.util.concurrent.ConcurrentHashMap;
14 | import java.util.regex.Pattern;
15 |
16 | import org.apache.commons.lang3.StringUtils;
17 |
18 | public class MBTilesUtils {
19 |
20 | private static final ConcurrentHashMap INSTANCES = new ConcurrentHashMap();
21 |
22 | private final Connection conn;
23 |
24 | private final PreparedStatement ps;
25 |
26 | private MBTilesUtils(String db) {
27 | try {
28 | Class.forName("org.sqlite.JDBC");
29 | } catch (ClassNotFoundException e) {
30 | throw new RuntimeException(e);
31 | }
32 |
33 | if (db == null || !new File(db).exists()) {
34 | throw new RuntimeException("No database");
35 | }
36 |
37 | try {
38 | conn = DriverManager.getConnection("jdbc:sqlite:" + db);
39 | } catch (SQLException e) {
40 | throw new RuntimeException(e);
41 | }
42 |
43 | try {
44 | ps = conn.prepareStatement("SELECT tile_data FROM tiles "
45 | + "WHERE zoom_level = ? AND tile_column = ? "
46 | + "AND tile_row = ?;");
47 | } catch (SQLException e) {
48 | throw new RuntimeException(e);
49 | }
50 | }
51 |
52 | static Map getDatabases() {
53 | Properties configuration = new Properties();
54 | try {
55 | configuration.load(MBTilesUtils.class.getClassLoader()
56 | .getResourceAsStream("mbtiles4j.properties"));
57 | } catch (IOException e) {
58 | throw new RuntimeException(e);
59 | }
60 |
61 | HashMap result = new HashMap();
62 |
63 | String dbs = configuration.getProperty("tile-dbs");
64 | if (StringUtils.isEmpty(dbs)) {
65 | return result;
66 | }
67 |
68 | String[] split = dbs.split(Pattern.quote(","));
69 | for (String entry : split) {
70 | String path = configuration.getProperty(entry + ".path");
71 | if (!StringUtils.isEmpty(path)) {
72 | result.put(entry, path);
73 | }
74 | }
75 |
76 | return result;
77 | }
78 |
79 | public static MBTilesUtils getInstance(String db) {
80 | return INSTANCES.get(db);
81 | }
82 |
83 | public synchronized byte[] getTiles(int x, int y, int z) {
84 | int index = 1;
85 |
86 | ResultSet rs = null;
87 | try {
88 | ps.setInt(index++, z);
89 | ps.setInt(index++, x);
90 | ps.setInt(index++, y);
91 |
92 | rs = ps.executeQuery();
93 | if (rs.next()) {
94 | return rs.getBytes(1);
95 | }
96 | } catch (SQLException e) {
97 | throw new RuntimeException(e);
98 | } finally {
99 | try {
100 | if (rs != null) {
101 | rs.close();
102 | }
103 | } catch (SQLException e) {
104 | e.printStackTrace();
105 | }
106 | }
107 |
108 | return null;
109 | }
110 |
111 | private synchronized void close() {
112 | if (ps != null) {
113 | try {
114 | ps.close();
115 | } catch (SQLException e) {
116 | e.printStackTrace();
117 | }
118 | }
119 | if (conn != null) {
120 | try {
121 | conn.close();
122 | } catch (SQLException e) {
123 | e.printStackTrace();
124 | }
125 | }
126 | }
127 |
128 | public static void connect() {
129 | Map dbs = getDatabases();
130 | for (String db : dbs.keySet()) {
131 | if (!INSTANCES.containsKey(db)) {
132 | INSTANCES.put(db, new MBTilesUtils(dbs.get(db)));
133 | }
134 | }
135 | }
136 |
137 | public static void disconnect() {
138 | for (MBTilesUtils entry : INSTANCES.values()) {
139 | entry.close();
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------