UNPKG

geotile_sdk

Version:

A tile sdk for Cloud Optimised Geotiff and shapefile

410 lines (379 loc) 12.4 kB
/* * @Description: 地图渲染和瓦片提取, png编码 * @Author: luojun1 * @Date: 2021-10-21 17:25:12 * @LastEditTime: 2021-12-15 15:50:32 */ var mapnik = require('mapnik-atlas') var path = require('path') var fs = require('fs') var SRS = require('./SRS') const zlib = require('zlib') // const stringRandom = require('string-random') mapnik.register_default_fonts() mapnik.register_default_input_plugins() var _styleDir = path.join(process.cwd(), 'style') // 通过程序接口动态加载图层 function addLayer (map, filepath, EPSG) { var layertype = 'gdal' if (path.extname(filepath) === '.shp') { layertype = 'shape' } else if (path.extname(filepath) === '.geojson') { layertype = 'geojson' } var options = { type: layertype, file: filepath } // create a layer var layer = new mapnik.Layer('world') layer.styles = ['My Style'] layer.srs = '+init=epsg:' + EPSG try { layer.datasource = new mapnik.Datasource(options) } catch (err) { console.log(err) return err } map.add_layer(layer) return null } function queryTile (map, requestBbox, sendRecall, tileSize, notEncode) { map.zoomToBox(requestBbox) if (!tileSize) { tileSize = [256, 256] } var tile = new mapnik.Image(tileSize[0], tileSize[1]) map.render(tile, function (err, tile) { if (err) { console.error('render failed') var blank = [] sendRecall(err, blank) } if (notEncode) { sendRecall(err, tile) } else { // var start = new Date().getTime() tile.encode('png', function (err, buffer) { // var processTime = new Date().getTime() // console.log('###encode Time:', processTime - start) sendRecall(err, buffer) }) } }) } function queryVTile (map, requestBbox, x, y, z, sendRecall) { var vtile = new mapnik.VectorTile(Number(z), Number(x), Number(y)) map.extent = requestBbox // [-11271098.442818949,4696291.017841229,-11192826.925854929,4774562.534805249]; map.render(vtile, {}, function (err, vtile) { if (err) { console.error('render failed') var blank = [] sendRecall(err, blank) } zlib.deflate(vtile.getData(), (err, data) => { sendRecall(err, data) }) }) } //* ******两种获取瓦片方法**********/ // 方法1:给定单个瓦片的xyz,一次获取单个瓦片 function fetchTile (x, y, z, xmlUUID, format, sendRecall) { if (xmlUUID != null && (xmlUUID)) { var xmlPath = path.join(_styleDir, xmlUUID + '.xml') fs.access(xmlPath, fs.constants.F_OK, (err) => { if (err) { var blank = [] console.error('access failed') sendRecall(err, blank) return } var map = new mapnik.Map(256, 256) map.load(xmlPath, function (err, map) { if (err) { console.error('loading failed: ', err) var blank = [] sendRecall(err, blank) return } // var err=addLayer(map, filepath, EPSG) map.aspect_fix_mode = mapnik.Map.ASPECT_RESPECT var requestBbox = SRS.tile2BBox([Number(x), Number(y), Number(z)]) // console.log("requestBbox:",requestBbox) console.log('format:', format) if (format === 'mvt') { queryVTile(map, requestBbox, x, y, z, sendRecall) } else { queryTile(map, requestBbox, sendRecall) } }) }) } else { var err = new Error('lack of xmlUUID') var blank = [] sendRecall(err, blank) } } // 方法2:给定多个瓦片的连续范围,一次获取多个瓦片 function fetchTiles (paddingRegion, z, xmlUUID, sendRecall) { var tileSize = [256, 256] var paddingRegionSize = [tileSize[0] * (paddingRegion[1] - paddingRegion[0]), tileSize[1] * (paddingRegion[3] - paddingRegion[2])] if (xmlUUID != null && (xmlUUID)) { var xmlPath = path.join(_styleDir, xmlUUID + '.xml') fs.access(xmlPath, fs.constants.F_OK, (err) => { if (err) { var blank = [] console.error('access failed') sendRecall(err, blank) return } var map = new mapnik.Map(paddingRegionSize[0], paddingRegionSize[1]) map.load(xmlPath, function (err, map) { if (err) { console.error('loading failed: ', err) var blank = [] sendRecall(err, blank) return } // var err=addLayer(map, filepath, EPSG) map.aspect_fix_mode = mapnik.Map.ASPECT_RESPECT var requestBbox = SRS.tile2BBox([Number(paddingRegion[0]), Number(paddingRegion[2]), Number(z)], [Number(paddingRegion[1]), Number(paddingRegion[3]), Number(z)]) // console.log("requestBbox:",requestBbox) queryTile(map, requestBbox, sendRecall, paddingRegionSize, true) // 暂不编码png }) }) } else { var err = new Error('lack of xmlUUID') var blank = [] sendRecall(err, blank) } } // 从大瓦片中获取小瓦片 function getBlock (Bigtile, offsetX, offsety, width, height, recall) { Bigtile.resize(width, height, {scaling_method: mapnik.imageScaling.near, filter_factor: 1.0, offset_x: Number(offsetX), offset_y: Number(offsety), offset_width: width, offset_height: height }, function (err, tile) { if (err) { console.log('getBlock:', err) } recall(err, tile) }) } // json字段定义 // var styleJson_raster = { // 'style': { // 'Type': 'Raster' // }, // 'layer': { // 'path': '/luojun/github/sl-component-vis-raster-v2/test/data/clip2_cog.tif', // 'SRS': '4326' // } // } // var styleJson_polygon = { // 'style': { // 'Type': 'Polygon', // 'Color': 'black', // 'LineWidth': 1, // 'FillColor': '#bee826' // }, // 'layer': { // 'path': '/luojun/github/geotiff-server/test/data/world_merc.json', // 'SRS': '3857' // } // } // var styleJson_line = { // 'style': { // 'Type': 'Line', // 'Color': '#bee826', // 'LineWidth': 1 // }, // 'layer': { // 'path': '/luojun/github/geotiff-server/test/data/world_merc.json', // 'SRS': '3857' // } // } // var styleJson_point = { // 'style': { // 'Type': 'Point', // 'Color': 'black', // 'LineWidth': 1 // }, // 'layer': { // 'path': '/luojun/github/geotiff-server/test/data/world_merc.json', // 'SRS': '3857' // } // } function generateXML (jsonStr, recall) { var builder = require('xmlbuilder') const random = require('string-random') var xmlUUID = random(8, {numbers: false}) const mapConfig = JSON.parse(jsonStr) var xml = builder.create('Map') .att('srs', '+init=epsg:3857') if (Array.isArray(mapConfig.style)) { for (let i = 0; i < mapConfig.style.length; i++) { const featureType = mapConfig.style[i].Type switch (featureType) { case 'Polygon': xml = xml.ele('Style', {'name': mapConfig.style[i].styleName}) .ele('Rule') .ele('PolygonSymbolizer', {'fill': mapConfig.style[i].FillColor}).up() .ele('LineSymbolizer', {'stroke': mapConfig.style[i].Color, 'stroke-width': mapConfig.style[i].LineWidth}).up() .up() .up() break case 'Line': xml = xml.ele('Style', {'name': mapConfig.style[i].styleName}) .ele('Rule') .ele('LineSymbolizer', {'stroke': mapConfig.style[i].Color, 'stroke-width': mapConfig.style[i].LineWidth}).up() .up() .up() break case 'Point': xml = xml.ele('Style', {'name': mapConfig.style[i].styleName}) .ele('Rule') .ele('PointSymbolizer').up() .up() .up() break case 'Raster': xml = xml.ele('Style', {'name': mapConfig.style[i].styleName}) .ele('Rule') .ele('RasterSymbolizer').up() .up() .up() break } } } if (Array.isArray(mapConfig.layer)) { for (let i = 0; i < mapConfig.layer.length; i++) { xml = xml.ele('Layer', {'name': xmlUUID + i.toString(), 'srs': '+init=epsg:' + mapConfig.layer[i].SRS}) .ele('StyleName', mapConfig.layer[i].style).up() .ele('Datasource') .ele('Parameter', {'name': 'type'}, mapConfig.layer[i].type).up() if (mapConfig.layer[i].hasOwnProperty('path')) { xml = xml.ele('Parameter', {'name': 'file'}, mapConfig.layer[i].path).up() } else { xml = xml.ele('Parameter', {'name': 'host'}, mapConfig.layer[i].host).up() .ele('Parameter', {'name': 'dbname'}, mapConfig.layer[i].dbname).up() .ele('Parameter', {'name': 'user'}, mapConfig.layer[i].user).up() .ele('Parameter', {'name': 'password'}, mapConfig.layer[i].password).up() .ele('Parameter', {'name': 'table'}, '(' + mapConfig.layer[i].sql + ') as world').up() } console.log(xml) xml = xml.up() .up() } } var xmlStr = xml.end({pretty: true}) var xmlPath = path.join(_styleDir, xmlUUID + '.xml') fs.access(xmlPath, fs.constants.F_OK, (err) => { if (err) { // console.log("xmlStr:",xmlStr) fs.writeFile(xmlPath, xmlStr, err => { recall(err, xmlUUID) }) } else { recall(null, xmlUUID) } }) } // function generateXML (jsonStr, recall) { // var builder = require('xmlbuilder') // const random = require('string-random') // var xmlUUID = random(8, {numbers: false}) // const mapConfig = JSON.parse(jsonStr) // const featureType = mapConfig.style.Type // var srcType = 'gdal' // if (path.extname(mapConfig.layer.path) === '.shp') { // srcType = 'shape' // } else if (path.extname(mapConfig.layer.path) === '.geojson') { // srcType = 'geojson' // } // var xml = builder.create('Map') // .att('srs', '+init=epsg:3857') // .ele('Style', {'name': 'My Style'}) // .ele('Rule') // switch (featureType) { // case 'Polygon': // xml.ele('PolygonSymbolizer', {'fill': mapConfig.style.FillColor}).up() // .ele('LineSymbolizer', {'stroke': mapConfig.style.Color, 'stroke-width': mapConfig.style.LineWidth}).up() // .up() // .up() // break // case 'Line': // xml.ele('LineSymbolizer', {'stroke': mapConfig.style.Color, 'stroke-width': mapConfig.style.LineWidth}).up() // .up() // .up() // break // case 'Point': // xml.ele('PointSymbolizer').up() // .up() // .up() // break // case 'Raster': // xml.ele('RasterSymbolizer').up() // .up() // .up() // break // } // xml.up() // .up() // .ele('Layer', {'name': xmlUUID, 'srs': '+init=epsg:' + mapConfig.layer.SRS}) // .ele('StyleName', 'My Style').up() // .ele('Datasource') // .ele('Parameter', {'name': 'file'}, mapConfig.layer.path).up() // .ele('Parameter', {'name': 'type'}, srcType).up() // var xmlStr = xml.end({pretty: true}) // var xmlPath = path.join(_styleDir, xmlUUID + '.xml') // fs.access(xmlPath, fs.constants.F_OK, (err) => { // if (err) { // // console.log("xmlStr:",xmlStr) // fs.writeFile(xmlPath, xmlStr, err => { // recall(err, xmlUUID) // }) // } else { // recall(null, xmlUUID) // } // }) // } function setMapnikDir (styleDir, mapnikDir) { if (mapnikDir && mapnikDir !== '') { mapnik.settings.paths = { 'fonts': path.join(mapnikDir, 'lib/mapnik/fonts'), 'input_plugins': path.join(mapnikDir, 'lib/mapnik/input'), 'mapnik_index': path.join(mapnikDir, 'bin/mapnik-index'), 'shape_index': path.join(mapnikDir, 'bin/shapeindex') } mapnik.settings.env = { 'ICU_DATA': path.join(mapnikDir, 'share/icu'), 'GDAL_DATA': path.join(mapnikDir, 'share/gdal'), 'PROJ_LIB': path.join(mapnikDir, 'share/proj') } } if (styleDir && styleDir !== '') _styleDir = styleDir var err = fs.accessSync(_styleDir, fs.constants.R_OK | fs.constants.F_OK) if (err) { console.log(`${_styleDir} 'is not readable'`) return err } err = fs.accessSync(mapnik.settings.paths.input_plugins, fs.constants.R_OK | fs.constants.F_OK) if (err) { console.log(`${mapnik.settings.paths.input_plugins} 'is not readable'`) return err } return null } module.exports = { fetchTile, fetchTiles, getBlock, generateXML, setMapnikDir, addLayer }