blob: 8e8a7063dc1f3a36426fed90086959e3b38e79fc [file] [log] [blame]
evansirokyd401c892016-06-16 00:05:14 -07001var fs = require('fs'),
2 http = require('http')
3
4var async = require('async'),
evansiroky63d35e12016-06-16 10:08:15 -07005 jsts = require('jsts'),
evansirokyd401c892016-06-16 00:05:14 -07006 overpass = require('query-overpass'),
7 shp = require('shpjs')
8
9
evansiroky63d35e12016-06-16 10:08:15 -070010var osmBoundarySources = require('./osmBoundarySources.json'),
evansiroky50216c62016-06-16 17:41:47 -070011 zoneCfg = require('./timezones.json'),
evansiroky63d35e12016-06-16 10:08:15 -070012 geoJsonReader = new jsts.io.GeoJSONReader(),
13 geoJsonWriter = new jsts.io.GeoJSONWriter()
14
15
evansirokyd401c892016-06-16 00:05:14 -070016var toArrayBuffer = function(buffer) {
17 var ab = new ArrayBuffer(buffer.length)
18 var view = new Uint8Array(ab)
19 for (var i = 0; i < buffer.length; ++i) {
20 view[i] = buffer[i]
21 }
22 return view
23}
24
25var extractToGeoJson = function(callback) {
26 shp(toArrayBuffer(fs.readFileSync('./downloads/tz_world_mp.zip')))
27 .then(function(geojson) { console.log('extract success'); callback(null, geojson) })
28 .catch(function(e){ console.log('extract err', e); callback(e) })
29}
30
evansiroky63d35e12016-06-16 10:08:15 -070031var union = function(a, b) {
32 var _a = geoJsonReader.read(JSON.stringify(a)),
33 _b = geoJsonReader.read(JSON.stringify(b))
34
35 var result = _a.union(_b)
36
37 return geoJsonWriter.write(result)
38
39}
40
evansiroky50216c62016-06-16 17:41:47 -070041var fetchIfNeeded = function(file, superCallback, fetchFn) {
42 fs.stat(file, function(err) {
43 if(!err) { return superCallback() }
44 fetchFn()
45 })
46}
47
evansiroky63d35e12016-06-16 10:08:15 -070048var downloadOsmBoundary = function(boundaryId, boundaryCallback) {
49 var cfg = osmBoundarySources[boundaryId],
50 query = '[out:json][timeout:60];',
51 boundaryFilename = './downloads/' + cfg.type,
52 debug = 'getting data for '
53
54 if(cfg.type === 'ISO3166-1') {
55 query += '(relation["boundary"="administrative"]' +
56 '["admin_level"="2"]' +
57 '["ISO3166-1"="' + cfg.code + '"]);' +
58 'out body;>;out meta qt;'
59 boundaryFilename += '_' + cfg.code
60 debug += 'country: ' + cfg.code
61 }
62
63 console.log(debug)
64
65 async.auto({
evansiroky50216c62016-06-16 17:41:47 -070066 downloadFromOverpass: function(results, cb) {
67 console.log('downloading from overpass')
68 fetchIfNeeded(boundaryFilename, cb, function() {
69 overpass(query, cb, { flatProperties: true })
evansiroky63d35e12016-06-16 10:08:15 -070070 })
71 },
evansiroky63d35e12016-06-16 10:08:15 -070072 validateOverpassResult: ['downloadFromOverpass', function(results, cb) {
73 var data = results.downloadFromOverpass
74 if(!data.features || data.features.length == 0) {
75 err = new Error('Invalid geojson for boundary: ' + boundaryId)
76 return cb(err)
77 }
78 cb()
79 }],
80 saveSingleMultiPolygon: ['validateOverpassResult', function(results, cb) {
81 var data = results.downloadFromOverpass,
82 combined
83
84 // union all multi-polygons / polygons into one
85 for (var i = data.features.length - 1; i >= 0; i--) {
86 var curGeom = data.features[i].geometry
87 if(curGeom.type === 'Polygon' || curGeom.type === 'MultiPolygon') {
88 console.log('combining border')
89 if(!combined) {
90 combined = curGeom
91 } else {
92 combined = union(curGeom, combined)
93 }
94 }
95 }
96 fs.writeFile(boundaryFilename, JSON.stringify(combined, null, 2), cb)
97 }]
98 }, boundaryCallback)
99}
evansirokyd401c892016-06-16 00:05:14 -0700100
101async.auto({
102 makeDownloadsDir: function(cb) {
103 console.log('creating downloads dir')
104 fs.mkdir('./downloads', function(err) {
105 if(err && err.code === 'EEXIST') {
106 cb()
107 } else {
108 cb(err)
109 }
110 })
111 },
112 getEfeleShapefile: ['makeDownloadsDir', function(results, cb) {
evansiroky50216c62016-06-16 17:41:47 -0700113 console.log('download efele.net shapefile')
114 var efeleFilename = './downloads/tz_world_mp.zip'
115 fetchIfNeeded(efeleFilename, cb, function() {
116 var file = fs.createWriteStream(efeleFilename)
117 http.get('http://efele.net/maps/tz/world/tz_world_mp.zip', function(response) {
118 response.pipe(file)
119 file
120 .on('finish', function() {
121 file.close(cb)
122 })
123 .on('error', cb)
124 })
evansirokyd401c892016-06-16 00:05:14 -0700125 })
126 }],
evansirokyd401c892016-06-16 00:05:14 -0700127 getOsmBoundaries: ['makeDownloadsDir', function(results, cb) {
128 console.log('downloading osm boundaries')
evansiroky50216c62016-06-16 17:41:47 -0700129 return cb()
evansiroky63d35e12016-06-16 10:08:15 -0700130 async.eachSeries(Object.keys(osmBoundarySources), downloadOsmBoundary, cb)
131 }],
132 extractEfeleNetShapefile: ['getOsmBoundaries', function(results, cb) {
evansiroky63d35e12016-06-16 10:08:15 -0700133 console.log('extracting efele.net shapefile')
134 extractToGeoJson(cb)
evansiroky50216c62016-06-16 17:41:47 -0700135 }],
136 dictifyEfeleNetData: ['extractEfeleNetShapefile', function(results, cb) {
137 var timezoneLookup = {}
138 for (var i = results.extractEfeleNetShapefile.features.length - 1; i >= 0; i--) {
139 var curTz = results.extractEfeleNetShapefile.features[i]
140 timezoneLookup[curTz.properties.TZID] = i
141 }
142 cb(null, timezoneLookup)
143 }],
144 createZones: ['dictifyEfeleNetData', function(results, cb) {
145 async.each(Object.keys(zoneCfg), makeTimezoneBoundary, cb)
146 }],
147 mergeZones: ['createZones', function(results, cb) {
148
evansirokyd401c892016-06-16 00:05:14 -0700149 }]
evansiroky50216c62016-06-16 17:41:47 -0700150}, function(err, results) {
evansirokyd401c892016-06-16 00:05:14 -0700151 console.log('done')
152 if(err) {
153 console.log('error!', err)
154 return
155 }
156
evansiroky50216c62016-06-16 17:41:47 -0700157 console.log(results.dictifyEfeleNetData)
evansirokyd401c892016-06-16 00:05:14 -0700158})