mirror of
https://github.com/NYCPlanning/labs-geojson2mvt.git
synced 2025-12-08 19:46:30 +00:00
first commit
This commit is contained in:
commit
8dbde79bca
8
.eslint
Normal file
8
.eslint
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
parser: "babel-eslint",
|
||||
"extends": "airbnb",
|
||||
"env": {
|
||||
"browser": false,
|
||||
"node": true
|
||||
},
|
||||
}
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
example/tiles
|
||||
41
example/README.md
Normal file
41
example/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
#geojson2mvt example
|
||||
|
||||
This example will cut vector tiles from a geojson file of NYC's bus routes.
|
||||
|
||||
## How to Use
|
||||
|
||||
Be sure to install dependencies on the `geojson2mvt` root directory
|
||||
`npm install`
|
||||
|
||||
Run the script
|
||||
`node script.js`
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
// require geojson2mvt
|
||||
var geojson2mvt = require('./src');
|
||||
|
||||
// specify the path to the geojson file
|
||||
const filePath = './bus_routes.geojson';
|
||||
|
||||
// create an options object
|
||||
var options = {
|
||||
rootDir: 'tiles',
|
||||
bbox : [40.426042,-74.599228,40.884448,-73.409958], //[south,west,north,east]
|
||||
zoom : {
|
||||
min : 8,
|
||||
max : 18,
|
||||
},
|
||||
layerName: 'layer0',
|
||||
};
|
||||
|
||||
// call geojson2mvt
|
||||
geojson2mvt(filePath, options);
|
||||
```
|
||||
##Try them out with Maputnik
|
||||
Also in this example directory is a modified python SimpleHTTPServer that will not have any CORS issues. Run it like `python cors_server.py` and it will create a webserver in this directory. The tile pyramid should now be available at `http://localhost:31338/tiles/{z}/{x}/{y}.mvt`
|
||||
|
||||
Create a new xyz Vector Tile source in Maputnik with this template, and you should be able to add a style with your freshly cut vector tiles.
|
||||
|
||||
Happy Mapping!
|
||||
1
example/bus_routes.geojson
Normal file
1
example/bus_routes.geojson
Normal file
File diff suppressed because one or more lines are too long
59
example/cors_server.py
Normal file
59
example/cors_server.py
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
import SimpleHTTPServer
|
||||
class CORSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
def send_head(self):
|
||||
"""Common code for GET and HEAD commands.
|
||||
This sends the response code and MIME headers.
|
||||
Return value is either a file object (which has to be copied
|
||||
to the outputfile by the caller unless the command was HEAD,
|
||||
and must be closed by the caller under all circumstances), or
|
||||
None, in which case the caller has nothing further to do.
|
||||
"""
|
||||
path = self.translate_path(self.path)
|
||||
f = None
|
||||
if os.path.isdir(path):
|
||||
if not self.path.endswith('/'):
|
||||
# redirect browser - doing basically what apache does
|
||||
self.send_response(301)
|
||||
self.send_header("Location", self.path + "/")
|
||||
self.end_headers()
|
||||
return None
|
||||
for index in "index.html", "index.htm":
|
||||
index = os.path.join(path, index)
|
||||
if os.path.exists(index):
|
||||
path = index
|
||||
break
|
||||
else:
|
||||
return self.list_directory(path)
|
||||
ctype = self.guess_type(path)
|
||||
try:
|
||||
# Always read in binary mode. Opening files in text mode may cause
|
||||
# newline translations, making the actual size of the content
|
||||
# transmitted *less* than the content-length!
|
||||
f = open(path, 'rb')
|
||||
except IOError:
|
||||
self.send_error(404, "File not found")
|
||||
return None
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", ctype)
|
||||
fs = os.fstat(f.fileno())
|
||||
self.send_header("Content-Length", str(fs[6]))
|
||||
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.end_headers()
|
||||
return f
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
import SocketServer
|
||||
|
||||
PORT = 31338
|
||||
|
||||
Handler = CORSHTTPRequestHandler
|
||||
#Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
|
||||
|
||||
httpd = SocketServer.TCPServer(("", PORT), Handler)
|
||||
|
||||
print "serving at port", PORT
|
||||
httpd.serve_forever()
|
||||
16
example/example.js
Normal file
16
example/example.js
Normal file
@ -0,0 +1,16 @@
|
||||
var geojson2mvt = require('./src');
|
||||
|
||||
const filePath = './bus_routes.geojson';
|
||||
|
||||
var options = {
|
||||
rootDir: 'tiles',
|
||||
bbox : [40.426042,-74.599228,40.884448,-73.409958], //[south,west,north,east]
|
||||
zoom : {
|
||||
min : 8,
|
||||
max : 18,
|
||||
},
|
||||
layerName: 'layer0',
|
||||
};
|
||||
|
||||
geojson2mvt(filePath, options);
|
||||
|
||||
16
example/script.js
Normal file
16
example/script.js
Normal file
@ -0,0 +1,16 @@
|
||||
var geojson2mvt = require('../src');
|
||||
|
||||
const filePath = './bus_routes.geojson';
|
||||
|
||||
var options = {
|
||||
rootDir: 'tiles',
|
||||
bbox : [40.426042,-74.599228,40.884448,-73.409958], //[south,west,north,east]
|
||||
zoom : {
|
||||
min : 8,
|
||||
max : 18,
|
||||
},
|
||||
layerName: 'layer0',
|
||||
};
|
||||
|
||||
geojson2mvt(filePath, options);
|
||||
|
||||
153
npm-debug.log
Normal file
153
npm-debug.log
Normal file
@ -0,0 +1,153 @@
|
||||
0 info it worked if it ends with ok
|
||||
1 verbose cli [ '/usr/local/Cellar/node/7.5.0/bin/node',
|
||||
1 verbose cli '/usr/local/bin/npm',
|
||||
1 verbose cli 'publish',
|
||||
1 verbose cli 'geojson2mvt' ]
|
||||
2 info using npm@4.1.2
|
||||
3 info using node@v7.5.0
|
||||
4 verbose publish [ 'geojson2mvt' ]
|
||||
5 silly cache add args [ 'geojson2mvt', null ]
|
||||
6 verbose cache add spec geojson2mvt
|
||||
7 silly cache add parsed spec Result {
|
||||
7 silly cache add raw: 'geojson2mvt',
|
||||
7 silly cache add scope: null,
|
||||
7 silly cache add escapedName: 'geojson2mvt',
|
||||
7 silly cache add name: 'geojson2mvt',
|
||||
7 silly cache add rawSpec: '',
|
||||
7 silly cache add spec: 'latest',
|
||||
7 silly cache add type: 'tag' }
|
||||
8 silly addNamed geojson2mvt@latest
|
||||
9 verbose addNamed "latest" is being treated as a dist-tag for geojson2mvt
|
||||
10 info addNameTag [ 'geojson2mvt', 'latest' ]
|
||||
11 silly mapToRegistry name geojson2mvt
|
||||
12 silly mapToRegistry using default registry
|
||||
13 silly mapToRegistry registry https://registry.npmjs.org/
|
||||
14 silly mapToRegistry data Result {
|
||||
14 silly mapToRegistry raw: 'geojson2mvt',
|
||||
14 silly mapToRegistry scope: null,
|
||||
14 silly mapToRegistry escapedName: 'geojson2mvt',
|
||||
14 silly mapToRegistry name: 'geojson2mvt',
|
||||
14 silly mapToRegistry rawSpec: '',
|
||||
14 silly mapToRegistry spec: 'latest',
|
||||
14 silly mapToRegistry type: 'tag' }
|
||||
15 silly mapToRegistry uri https://registry.npmjs.org/geojson2mvt
|
||||
16 verbose addNameTag registry:https://registry.npmjs.org/geojson2mvt not in flight; fetching
|
||||
17 verbose request uri https://registry.npmjs.org/geojson2mvt
|
||||
18 verbose request no auth needed
|
||||
19 info attempt registry request try #1 at 11:36:06 AM
|
||||
20 verbose request using bearer token for auth
|
||||
21 verbose request id d08f23ced3a68e65
|
||||
22 http request GET https://registry.npmjs.org/geojson2mvt
|
||||
23 http 200 https://registry.npmjs.org/geojson2mvt
|
||||
24 verbose headers { server: 'nginx/1.10.1',
|
||||
24 verbose headers 'content-type': 'application/json',
|
||||
24 verbose headers 'last-modified': 'Sat, 04 Mar 2017 16:34:57 GMT',
|
||||
24 verbose headers etag: 'W/"58baecb1-4a0"',
|
||||
24 verbose headers 'content-encoding': 'gzip',
|
||||
24 verbose headers 'cache-control': 'max-age=300',
|
||||
24 verbose headers 'transfer-encoding': 'chunked',
|
||||
24 verbose headers 'accept-ranges': 'bytes',
|
||||
24 verbose headers date: 'Sat, 04 Mar 2017 16:36:06 GMT',
|
||||
24 verbose headers via: '1.1 varnish',
|
||||
24 verbose headers connection: 'keep-alive',
|
||||
24 verbose headers 'x-served-by': 'cache-dfw1836-DFW',
|
||||
24 verbose headers 'x-cache': 'MISS',
|
||||
24 verbose headers 'x-cache-hits': '0',
|
||||
24 verbose headers 'x-timer': 'S1488645366.318982,VS0,VE32',
|
||||
24 verbose headers vary: 'Accept-Encoding' }
|
||||
25 silly get cb [ 200,
|
||||
25 silly get { server: 'nginx/1.10.1',
|
||||
25 silly get 'content-type': 'application/json',
|
||||
25 silly get 'last-modified': 'Sat, 04 Mar 2017 16:34:57 GMT',
|
||||
25 silly get etag: 'W/"58baecb1-4a0"',
|
||||
25 silly get 'content-encoding': 'gzip',
|
||||
25 silly get 'cache-control': 'max-age=300',
|
||||
25 silly get 'transfer-encoding': 'chunked',
|
||||
25 silly get 'accept-ranges': 'bytes',
|
||||
25 silly get date: 'Sat, 04 Mar 2017 16:36:06 GMT',
|
||||
25 silly get via: '1.1 varnish',
|
||||
25 silly get connection: 'keep-alive',
|
||||
25 silly get 'x-served-by': 'cache-dfw1836-DFW',
|
||||
25 silly get 'x-cache': 'MISS',
|
||||
25 silly get 'x-cache-hits': '0',
|
||||
25 silly get 'x-timer': 'S1488645366.318982,VS0,VE32',
|
||||
25 silly get vary: 'Accept-Encoding' } ]
|
||||
26 verbose get saving geojson2mvt to /Users/chriswhong/.npm/registry.npmjs.org/geojson2mvt/.cache.json
|
||||
27 verbose correctMkdir /Users/chriswhong/.npm correctMkdir not in flight; initializing
|
||||
28 silly addNameTag next cb for geojson2mvt with tag latest
|
||||
29 silly addNamed geojson2mvt@1.0.0
|
||||
30 verbose addNamed "1.0.0" is a plain semver version for geojson2mvt
|
||||
31 silly cache afterAdd geojson2mvt@1.0.0
|
||||
32 verbose afterAdd /Users/chriswhong/.npm/geojson2mvt/1.0.0/package/package.json not in flight; writing
|
||||
33 verbose correctMkdir /Users/chriswhong/.npm correctMkdir not in flight; initializing
|
||||
34 verbose afterAdd /Users/chriswhong/.npm/geojson2mvt/1.0.0/package/package.json written
|
||||
35 silly publish { name: 'geojson2mvt',
|
||||
35 silly publish version: '1.0.0',
|
||||
35 silly publish main: 'index.js',
|
||||
35 silly publish scripts: { test: 'echo "Error: no test specified" && exit 1' },
|
||||
35 silly publish author: '',
|
||||
35 silly publish license: 'ISC',
|
||||
35 silly publish dependencies: { 'geojson-vt': '^2.4.0' },
|
||||
35 silly publish readme: 'ERROR: No README data found!',
|
||||
35 silly publish readmeFilename: 'README.md',
|
||||
35 silly publish _id: 'geojson2mvt@1.0.0',
|
||||
35 silly publish _shasum: '3096104c6a5592b995156bba6f294f2920b1893e',
|
||||
35 silly publish _from: 'geojson2mvt@latest' }
|
||||
36 verbose getPublishConfig undefined
|
||||
37 silly mapToRegistry name geojson2mvt
|
||||
38 silly mapToRegistry using default registry
|
||||
39 silly mapToRegistry registry https://registry.npmjs.org/
|
||||
40 silly mapToRegistry data Result {
|
||||
40 silly mapToRegistry raw: 'geojson2mvt',
|
||||
40 silly mapToRegistry scope: null,
|
||||
40 silly mapToRegistry escapedName: 'geojson2mvt',
|
||||
40 silly mapToRegistry name: 'geojson2mvt',
|
||||
40 silly mapToRegistry rawSpec: '',
|
||||
40 silly mapToRegistry spec: 'latest',
|
||||
40 silly mapToRegistry type: 'tag' }
|
||||
41 silly mapToRegistry uri https://registry.npmjs.org/geojson2mvt
|
||||
42 verbose publish registryBase https://registry.npmjs.org/
|
||||
43 silly publish uploading /Users/chriswhong/.npm/geojson2mvt/1.0.0/package.tgz
|
||||
44 verbose request uri https://registry.npmjs.org/geojson2mvt
|
||||
45 verbose request sending authorization for write operation
|
||||
46 info attempt registry request try #1 at 11:36:06 AM
|
||||
47 verbose request using bearer token for auth
|
||||
48 http request PUT https://registry.npmjs.org/geojson2mvt
|
||||
49 http 403 https://registry.npmjs.org/geojson2mvt
|
||||
50 verbose headers { 'content-type': 'application/json',
|
||||
50 verbose headers 'cache-control': 'max-age=300',
|
||||
50 verbose headers 'content-length': '95',
|
||||
50 verbose headers 'accept-ranges': 'bytes',
|
||||
50 verbose headers date: 'Sat, 04 Mar 2017 16:36:21 GMT',
|
||||
50 verbose headers via: '1.1 varnish',
|
||||
50 verbose headers connection: 'keep-alive',
|
||||
50 verbose headers 'x-served-by': 'cache-dfw1836-DFW',
|
||||
50 verbose headers 'x-cache': 'MISS',
|
||||
50 verbose headers 'x-cache-hits': '0',
|
||||
50 verbose headers 'x-timer': 'S1488645366.649672,VS0,VE14372',
|
||||
50 verbose headers vary: 'Accept-Encoding' }
|
||||
51 verbose request invalidating /Users/chriswhong/.npm/registry.npmjs.org/geojson2mvt on PUT
|
||||
52 error publish Failed PUT 403
|
||||
53 verbose stack Error: "You cannot publish over the previously published version 1.0.0." : geojson2mvt
|
||||
53 verbose stack at makeError (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:304:12)
|
||||
53 verbose stack at CachingRegistryClient.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:292:14)
|
||||
53 verbose stack at Request._callback (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:212:14)
|
||||
53 verbose stack at Request.self.callback (/usr/local/lib/node_modules/npm/node_modules/request/request.js:186:22)
|
||||
53 verbose stack at emitTwo (events.js:106:13)
|
||||
53 verbose stack at Request.emit (events.js:192:7)
|
||||
53 verbose stack at Request.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/request/request.js:1081:10)
|
||||
53 verbose stack at emitOne (events.js:96:13)
|
||||
53 verbose stack at Request.emit (events.js:189:7)
|
||||
53 verbose stack at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/request/request.js:1001:12)
|
||||
54 verbose statusCode 403
|
||||
55 verbose pkgid geojson2mvt
|
||||
56 verbose cwd /Users/chriswhong/Sites/geojson2mvt
|
||||
57 error Darwin 15.6.0
|
||||
58 error argv "/usr/local/Cellar/node/7.5.0/bin/node" "/usr/local/bin/npm" "publish" "geojson2mvt"
|
||||
59 error node v7.5.0
|
||||
60 error npm v4.1.2
|
||||
61 error code E403
|
||||
62 error "You cannot publish over the previously published version 1.0.0." : geojson2mvt
|
||||
63 error If you need help, you may report this error at:
|
||||
63 error <https://github.com/npm/npm/issues>
|
||||
64 verbose exit [ 1, true ]
|
||||
14
package.json
Normal file
14
package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "geojson2mvt",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"geojson-vt": "^2.4.0"
|
||||
}
|
||||
}
|
||||
24
src/helpers.js
Normal file
24
src/helpers.js
Normal file
@ -0,0 +1,24 @@
|
||||
var helpers = {
|
||||
//given a bounding box and zoom level, calculate x and y tile ranges
|
||||
getTileBounds(bbox, zoom) {
|
||||
var tileBounds = {
|
||||
xMin: this.long2tile(bbox[1], zoom),
|
||||
xMax: this.long2tile(bbox[3], zoom),
|
||||
yMin: this.lat2tile(bbox[2], zoom),
|
||||
yMax: this.lat2tile(bbox[0], zoom),
|
||||
};
|
||||
return tileBounds;
|
||||
},
|
||||
|
||||
//lookup tile name based on lat/lon, courtesy of http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers
|
||||
long2tile(lon,zoom) {
|
||||
return (Math.floor((lon+180)/360*Math.pow(2,zoom)));
|
||||
},
|
||||
|
||||
lat2tile(lat,zoom) {
|
||||
return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom)));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
module.exports = helpers;
|
||||
83
src/index.js
Normal file
83
src/index.js
Normal file
@ -0,0 +1,83 @@
|
||||
var fs = require('fs');
|
||||
var vtpbf = require('vt-pbf');
|
||||
var geojsonvt = require('geojson-vt');
|
||||
|
||||
var helpers = require('./helpers.js');
|
||||
|
||||
var geojson2mvt = function(filePath, options) {
|
||||
|
||||
var geoJson = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
var tileIndex = geojsonvt(geoJson);
|
||||
|
||||
// create the "root directory" to place downloaded tiles in
|
||||
try {fs.mkdirSync(options.rootDir, 0777);}
|
||||
catch(err){
|
||||
if (err.code !== 'EEXIST') callback(err);
|
||||
}
|
||||
|
||||
var tileCount = 0,
|
||||
tileCoords = {},
|
||||
tileBounds;
|
||||
|
||||
for(var z=options.zoom.min; z<=options.zoom.max; z++) {
|
||||
|
||||
//create z directory in the root directory
|
||||
var zPath = `${options.rootDir}/${z.toString()}/`;
|
||||
try{ fs.mkdirSync(zPath, 0777) }
|
||||
catch (err){
|
||||
if (err.code !== 'EEXIST') callback(err);
|
||||
}
|
||||
|
||||
// get the x and y bounds for the current zoom level
|
||||
var tileBounds = helpers.getTileBounds(options.bbox, z);
|
||||
console.log(tileBounds)
|
||||
|
||||
// x loop
|
||||
for(var x=tileBounds.xMin; x<=tileBounds.xMax; x++) {
|
||||
|
||||
// create x directory in the z directory
|
||||
var xPath = zPath + x.toString();
|
||||
try{ fs.mkdirSync(xPath, 0777) }
|
||||
catch (err){
|
||||
if (err.code !== 'EEXIST') callback(err);
|
||||
}
|
||||
|
||||
|
||||
// y loop
|
||||
for(var y=tileBounds.yMin; y<=tileBounds.yMax; y++) {
|
||||
|
||||
console.log(`Getting tile ${z} ${x} ${y} `)
|
||||
var mvt = getTile(z, x, y, tileIndex, options);
|
||||
|
||||
// TODO what should be written to the tile if there is no data?
|
||||
var output = mvt !== null ? mvt : '';
|
||||
fs.writeFileSync(`${xPath}/${y}.mvt`, output);
|
||||
tileCount++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Done! I made ' + tileCount + ' tiles!');
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function getTile(z, x, y, tileIndex, options) {
|
||||
var tile = tileIndex.getTile(z, x, y);
|
||||
|
||||
if (tile != null) {
|
||||
var pbfOptions = {};
|
||||
|
||||
pbfOptions[options.layerName] = tile;
|
||||
var pbf = vtpbf.fromGeojsonVt(pbfOptions);
|
||||
|
||||
return pbf;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
module.exports = geojson2mvt;
|
||||
Loading…
x
Reference in New Issue
Block a user