first commit

This commit is contained in:
Chris Whong 2017-03-04 11:37:51 -05:00
commit 8dbde79bca
12 changed files with 417 additions and 0 deletions

8
.eslint Normal file
View File

@ -0,0 +1,8 @@
{
parser: "babel-eslint",
"extends": "airbnb",
"env": {
"browser": false,
"node": true
},
}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
example/tiles

0
README.md Normal file
View File

41
example/README.md Normal file
View 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!

File diff suppressed because one or more lines are too long

59
example/cors_server.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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;