├── .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 | --------------------------------------------------------------------------------