mirror of
https://github.com/cambecc/air.git
synced 2025-12-08 21:26:22 +00:00
add animated vector map, geo JSON translator, station long/lat data
This commit is contained in:
parent
fbf0db8e4f
commit
1cbd3c0f9b
134
api.js
134
api.js
@ -17,22 +17,59 @@ exports.initialize = function(stationsTableSpec, samplesTableSpec) {
|
||||
return this;
|
||||
}
|
||||
|
||||
function prepare(value) {
|
||||
return value;
|
||||
// return JSON.stringify(value, null, ' ');
|
||||
}
|
||||
|
||||
app.get('/about/stations', function(request, response) {
|
||||
var schema = {};
|
||||
stationsTable.columns.forEach(function(column) {
|
||||
schema[column.name] = column.description;
|
||||
});
|
||||
response.send(schema);
|
||||
response.type('json');
|
||||
response.json(prepare(schema));
|
||||
});
|
||||
|
||||
app.get('/stations', function(request, response) {
|
||||
var stmt = db.selectAll(stationsTable);
|
||||
when(db.execute(stmt)).then(
|
||||
function(result) {
|
||||
response.send(result.rows);
|
||||
response.type('json');
|
||||
response.json(prepare(result.rows));
|
||||
},
|
||||
function(error) {
|
||||
response.send(error.message);
|
||||
response.type('json');
|
||||
response.json(prepare(error.message));
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/stations/geo', function(request, response) {
|
||||
var stmt = db.selectAll(stationsTable);
|
||||
when(db.execute(stmt)).then(
|
||||
function(result) {
|
||||
var out = {
|
||||
type: 'FeatureCollection',
|
||||
features: result.rows.map(function(element) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: {name: element.id.toString()},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
parseFloat(element.longitude),
|
||||
parseFloat(element.latitude)
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
response.type('json');
|
||||
response.json(prepare(out));
|
||||
},
|
||||
function(error) {
|
||||
response.type('json');
|
||||
response.json(prepare(error.message));
|
||||
});
|
||||
});
|
||||
|
||||
@ -41,7 +78,8 @@ app.get('/about/samples', function(request, response) {
|
||||
samplesTable.columns.forEach(function(column) {
|
||||
schema[column.name] = column.description;
|
||||
});
|
||||
response.send(schema);
|
||||
response.type('json');
|
||||
response.json(prepare(schema));
|
||||
});
|
||||
|
||||
app.get('/samples/*', function(request, response) {
|
||||
@ -78,7 +116,7 @@ app.get('/samples/*', function(request, response) {
|
||||
function parseSampleTypePath() {
|
||||
result.sampleType = next; // UNDONE: sample type validation -- must be one of no, no2, temp, etc.
|
||||
result.stationId = args.shift(); // UNDONE: stationId validation -- must be numeric
|
||||
return db.selectSamples(samplesTable, result);
|
||||
return db.selectSamples(samplesTable, stationsTable, result);
|
||||
}
|
||||
|
||||
function parseDatePath() {
|
||||
@ -116,17 +154,97 @@ app.get('/samples/*', function(request, response) {
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
return response.send(result.error);
|
||||
response.type('json');
|
||||
return response.json(prepare(result.error));
|
||||
}
|
||||
|
||||
when(db.execute(stmt)).then(
|
||||
function(result) {
|
||||
response.send(result.rows);
|
||||
response.type('json');
|
||||
response.json(prepare(result.rows));
|
||||
},
|
||||
function(error) {
|
||||
response.send(error.message);
|
||||
response.type('json');
|
||||
response.json(prepare(error.message));
|
||||
});
|
||||
});
|
||||
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
var server = require('http').Server(app);
|
||||
var io = require('socket.io').listen(server);
|
||||
|
||||
// listen for incoming connections from client
|
||||
io.sockets.on('connection', function (socket) {
|
||||
|
||||
// start listening for coords
|
||||
socket.on('send:coords', function (data) {
|
||||
|
||||
when(db.execute(db.selectAll(stationsTable))).then(
|
||||
function(result) {
|
||||
var coords = [];
|
||||
result.rows.forEach(function(row) {
|
||||
if (row.latitude && row.longitude) {
|
||||
coords.push({lat: row.latitude, lng: row.longitude, acr: 0});
|
||||
}
|
||||
});
|
||||
var data = {id: 'stations', active: true, coords: coords};
|
||||
console.log('broadcast: ' + util.inspect(data, {depth:null}));
|
||||
socket.broadcast.emit('load:coords', data);
|
||||
},
|
||||
console.error);
|
||||
|
||||
// broadcast your coordinates to everyone except you
|
||||
socket.broadcast.emit('load:coords', data);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/wind/vectors', function(request, response) {
|
||||
// var args = request.params[0].split(/\//);
|
||||
// console.log('/points/* ' + util.inspect(args));
|
||||
|
||||
var constraints = {
|
||||
date: {current: true, parts: [], zone: '+09:00'},
|
||||
sampleType: null,
|
||||
stationId: null,
|
||||
error: null
|
||||
};
|
||||
|
||||
var selected = db.selectSamples(samplesTable, stationsTable, constraints);
|
||||
if (constraints.error) {
|
||||
response.json(constraints.error);
|
||||
return;
|
||||
}
|
||||
var respond = response.json.bind(response);
|
||||
db.execute(selected).then(buildVectors).then(respond, respond);
|
||||
});
|
||||
|
||||
function buildVectors(selectedSamples) {
|
||||
console.log('building vectors...');
|
||||
var d = when.defer();
|
||||
|
||||
db.execute(db.selectAll(stationsTable)).then(
|
||||
function(selectedStations) {
|
||||
var stations = {};
|
||||
selectedStations.rows.forEach(function(element) {
|
||||
stations[element.id] = [element.longitude, element.latitude];
|
||||
});
|
||||
var samples = [];
|
||||
selectedSamples.rows.forEach(function(sample) {
|
||||
if (sample.wv && sample.wd) {
|
||||
var coords = stations[sample.stationId];
|
||||
if (coords) {
|
||||
samples.push([coords[0] * 1, coords[1] * 1, [sample.wd * 1, sample.wv * 1], sample.stationId]);
|
||||
}
|
||||
}
|
||||
});
|
||||
d.resolve(samples);
|
||||
},
|
||||
function(error) {
|
||||
d.reject(error);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Listening on port 3000...');
|
||||
|
||||
10
db.js
10
db.js
@ -191,12 +191,15 @@ function sampleTypeConstraint(constraints) {
|
||||
* stationId: Number id of desired station, or null for all.
|
||||
* @returns {{sql: string, args: Array}} an object {sql: x, args: y} representing a sample select statement.
|
||||
*/
|
||||
exports.selectSamples = function(tableSpec, constraints) {
|
||||
exports.selectSamples = function(tableSpec, stationTableSpec, constraints) {
|
||||
console.log(constraints);
|
||||
var dateColumn = quoteName('date');
|
||||
var idColumn = quoteName('id');
|
||||
var stationIdColumn = quoteName('stationId');
|
||||
var longitudeColumn = quoteName('longitude');
|
||||
var latitudeColumn = quoteName('latitude');
|
||||
var table = quoteName(tableSpec.name);
|
||||
var stmt = 'SELECT ';
|
||||
var stmt = tool.format('SELECT b.{0}, b.{1}, ', longitudeColumn, latitudeColumn);
|
||||
|
||||
// First, decide which columns to select.
|
||||
if (constraints.sampleType && constraints.sampleType != 'all') {
|
||||
@ -215,7 +218,8 @@ exports.selectSamples = function(tableSpec, constraints) {
|
||||
}
|
||||
|
||||
// Next, constrain the results by date, station id, and sample type, where necessary.
|
||||
stmt += tool.format('\nFROM {0} WHERE {1}', table, dateConstraint(constraints.date));
|
||||
stmt += tool.format('\nFROM {0} a INNER JOIN {1} b ON a.{2} = b.{3}', table, quoteName(stationTableSpec.name), stationIdColumn, idColumn);
|
||||
stmt += tool.format('\nWHERE {0}', dateConstraint(constraints.date));
|
||||
var stationConstraint = stationIdConstraint(constraints);
|
||||
if (stationConstraint) {
|
||||
stmt += ' AND ' + stationConstraint;
|
||||
|
||||
30
ksj/bin/ksj
Executable file
30
ksj/bin/ksj
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// ./ksj -c ../../scratch/N03-110331_13.xml tk.json
|
||||
// ./ksj -t ./tk.json tk.geojson
|
||||
// topojson -o tk.topojson tk.geojson
|
||||
|
||||
var fs = require('fs');
|
||||
var when = require('when');
|
||||
var ksj = require('../ksj');
|
||||
|
||||
function write(file, data) {
|
||||
var d = when.defer();
|
||||
fs.writeFile(file, data, function(error) {
|
||||
if (error) {
|
||||
return d.reject(error);
|
||||
}
|
||||
d.resolve(true);
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
if (process.argv[2] === '-c') {
|
||||
ksj.convertToJSON(process.argv[3]).then(write.bind(null, process.argv[4]));
|
||||
}
|
||||
else if (process.argv[2] === '-t') {
|
||||
when(ksj.convertToGeoJSON(require(process.argv[3]))).then(write.bind(null, process.argv[4]));
|
||||
}
|
||||
else {
|
||||
console.log('wrong.');
|
||||
}
|
||||
355
ksj/ksj.js
Normal file
355
ksj/ksj.js
Normal file
@ -0,0 +1,355 @@
|
||||
'use strict';
|
||||
|
||||
var _ = require('underscore');
|
||||
var util = require('util');
|
||||
var when = require('when');
|
||||
var fs = require('fs');
|
||||
var xml2js = require('xml2js');
|
||||
|
||||
/**
|
||||
* Returns a promise for a simplified JSON representation of the specified KSJ xml file.
|
||||
*
|
||||
* @param sourceFile
|
||||
* @returns {*}
|
||||
*/
|
||||
exports.convertToJSON = function(sourceFile) {
|
||||
var d = when.defer();
|
||||
fs.readFile(sourceFile, function(error, data) {
|
||||
if (error) {
|
||||
return d.reject(error);
|
||||
}
|
||||
console.log('Parsing...');
|
||||
var parser = new xml2js.Parser();
|
||||
parser.parseString(data, function(error, root) {
|
||||
if (error) {
|
||||
return d.reject(error);
|
||||
}
|
||||
console.log('Process Pass 1...');
|
||||
var childCounts = pass1(root);
|
||||
console.log('Process Pass 2...');
|
||||
pass2(root, childCounts);
|
||||
console.log('Process Pass 3...');
|
||||
pass3(root);
|
||||
console.log('Stringify...');
|
||||
var result = JSON.stringify(root, null, ' ');
|
||||
console.log('Converted.');
|
||||
d.resolve(result);
|
||||
});
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
function removeNamespace(str) {
|
||||
var i = str.indexOf(':');
|
||||
return i < 0 ? str : str.substr(i + 1);
|
||||
}
|
||||
|
||||
function removePrefix(str) {
|
||||
var i = str.indexOf('.');
|
||||
return i < 0 ? str : str.substr(i + 1);
|
||||
}
|
||||
|
||||
function pass1(root) {
|
||||
// removes namespaces
|
||||
// removes prefixes
|
||||
// calculates child counts to aid in collapsing arrays during pass 2
|
||||
|
||||
var childCounts = {};
|
||||
|
||||
function visitArray(ary, context) {
|
||||
if (context) {
|
||||
childCounts[context] = Math.max(childCounts[context] | 0, ary.length);
|
||||
}
|
||||
ary.forEach(function(element, i) {
|
||||
ary[i] = visit(element);
|
||||
});
|
||||
return ary;
|
||||
}
|
||||
|
||||
function visitObject(obj, context) {
|
||||
_.keys(obj).forEach(function(rawKey) {
|
||||
var simpleKey = removePrefix(removeNamespace(rawKey));
|
||||
if (_.has(obj, simpleKey)) {
|
||||
simpleKey = rawKey; // simplified key already exists, so use original key as-is.
|
||||
}
|
||||
obj[simpleKey] = visit(obj[rawKey], simpleKey);
|
||||
if (simpleKey !== rawKey) {
|
||||
delete obj[rawKey];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
function visit(value, context) {
|
||||
if (_.isArray(value)) {
|
||||
return visitArray(value, context);
|
||||
}
|
||||
if (_.isObject(value)) {
|
||||
return visitObject(value, context);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
visit(root);
|
||||
|
||||
return childCounts;
|
||||
}
|
||||
|
||||
function pass2(root, childCounts) {
|
||||
// removes redundant arrays where all instances contain just one child
|
||||
// merges attributes ($) into owning object
|
||||
|
||||
function visitArray(ary, context) {
|
||||
ary.forEach(function(element, i) {
|
||||
ary[i] = visit(element);
|
||||
});
|
||||
return ary.length == 1 && childCounts[context] == 1 ? ary[0] : ary;
|
||||
}
|
||||
|
||||
function visitObject(obj, context) {
|
||||
var result = obj;
|
||||
_.keys(obj).forEach(function(key) {
|
||||
obj[key] = visit(obj[key], key);
|
||||
});
|
||||
|
||||
// Move all properties from $ into owning object if they don't already exist. If afterwards $ is
|
||||
// empty, then get rid of $.
|
||||
var $ = obj.$;
|
||||
if ($) {
|
||||
result = {}; // create a new object to represent the union of the keys in obj and obj.$
|
||||
_.keys($).forEach(function(key) {
|
||||
if (_.has(obj, key)) {
|
||||
return;
|
||||
}
|
||||
result[key] = $[key];
|
||||
delete $[key];
|
||||
});
|
||||
if (_.size($) === 0) {
|
||||
delete obj.$;
|
||||
}
|
||||
_.keys(obj).forEach(function(key) {
|
||||
result[key] = obj[key];
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function visit(value, context) {
|
||||
if (_.isArray(value)) {
|
||||
return visitArray(value, context);
|
||||
}
|
||||
if (_.isObject(value)) {
|
||||
return visitObject(value, context);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
visit(root);
|
||||
}
|
||||
|
||||
function pass3(root) {
|
||||
// simplifies deep objects to shallow objects
|
||||
|
||||
function visitArray(ary, context) {
|
||||
ary.forEach(function(element, i) {
|
||||
ary[i] = visit(element);
|
||||
});
|
||||
return ary;
|
||||
}
|
||||
|
||||
function visitObject(obj, context) {
|
||||
_.keys(obj).forEach(function(key) {
|
||||
obj[key] = visit(obj[key], key);
|
||||
});
|
||||
|
||||
var size = _.size(obj);
|
||||
|
||||
if (size === 2 && _.has(obj, 'dimension') && _.has(obj, 'coordinate')) {
|
||||
// {"coordinate": "35.89 139.01", "dimension": "2"} ==> "35.89 139.01"
|
||||
return obj.coordinate;
|
||||
}
|
||||
|
||||
if (size === 1 && _.has(obj, 'idref')) {
|
||||
// {"idref": "n00001"} ==> "n00001"
|
||||
return obj.idref;
|
||||
}
|
||||
|
||||
if (size === 1 && _.has(obj, 'DirectPosition') && context == 'position') {
|
||||
// "position": {"DirectPosition": "35.89 139.01"} ==> "position": "35.89 139.01"
|
||||
return obj.DirectPosition;
|
||||
}
|
||||
|
||||
if (size === 1 && _.has(obj, 'point') && context == 'indirect') {
|
||||
// "indirect": {"point": "n00001"} ==> "indirect": "n00001"
|
||||
return obj.point;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function visit(value, context) {
|
||||
if (_.isArray(value)) {
|
||||
return visitArray(value, context);
|
||||
}
|
||||
if (_.isObject(value)) {
|
||||
return visitObject(value, context);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
visit(root);
|
||||
}
|
||||
|
||||
exports.convertToGeoJSON = function(root) {
|
||||
var pointRefs = extractPointRefs(root);
|
||||
var curves = extractCurves(root, pointRefs);
|
||||
var surfaces = extractSurfaces(root);
|
||||
var names = extractNames(root, surfaces);
|
||||
var result = {
|
||||
type: 'FeatureCollection',
|
||||
features: buildFeatures(names, curves)
|
||||
};
|
||||
return JSON.stringify(result, null, ' ');
|
||||
}
|
||||
|
||||
function asPoint(str) {
|
||||
var point = str.split(' ').map(function(element) {
|
||||
return parseFloat(element);
|
||||
});
|
||||
// [long, lat] expected, but data source is [lat, long], so swap.
|
||||
var t = point[0];
|
||||
point[0] = point[1];
|
||||
point[1] = t;
|
||||
return point;
|
||||
}
|
||||
|
||||
function extractPointRefs(root) {
|
||||
var refs = {};
|
||||
var gmPoints = root.GI.dataset.object.AA01.OBJ.GM_Point;
|
||||
gmPoints.forEach(function(element) {
|
||||
refs[element.id] = asPoint(element.position);
|
||||
});
|
||||
return refs;
|
||||
}
|
||||
|
||||
function extractSegment(segment, pointRefs) {
|
||||
var gmPointArray = segment.GM_LineString.controlPoint.GM_PointArray.column;
|
||||
return gmPointArray.map(function(element) {
|
||||
var ref = element.indirect;
|
||||
if (ref) {
|
||||
return pointRefs[ref];
|
||||
}
|
||||
return asPoint(element.direct);
|
||||
});
|
||||
}
|
||||
|
||||
function extractCurves(root, pointRefs) {
|
||||
var curves = {};
|
||||
var gmCurves = root.GI.dataset.object.AA01.OBJ.GM_Curve;
|
||||
gmCurves.forEach(function(element) {
|
||||
var id = element.id;
|
||||
curves[id] = {id: element.id, points: extractSegment(element.segment, pointRefs)};
|
||||
});
|
||||
return curves;
|
||||
}
|
||||
|
||||
function extractCurveRefs(gmPolygon) {
|
||||
return gmPolygon.map(function(element) {
|
||||
var ref = element.boundary.GM_SurfaceBoundary.exterior.GM_Ring.generator;
|
||||
if (ref.indexOf('_') === 0) {
|
||||
ref = ref.substr(1);
|
||||
}
|
||||
return ref;
|
||||
});
|
||||
}
|
||||
|
||||
function extractSurfaces(root) {
|
||||
var surfaces = {};
|
||||
var gmSurfaces = root.GI.dataset.object.AA01.OBJ.GM_Surface;
|
||||
gmSurfaces.forEach(function(element) {
|
||||
var id = element.id;
|
||||
surfaces[id] = {id: element.id, curves: extractCurveRefs(element.patch.GM_Polygon)};
|
||||
});
|
||||
return surfaces;
|
||||
}
|
||||
|
||||
function scoalesce(x, y) {
|
||||
if (x && x.length > 0) {
|
||||
return x;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
function extractNames(root, surfaces) {
|
||||
var names = {};
|
||||
var ec01 = root.GI.dataset.object.AA01.OBJ.EC01;
|
||||
ec01.forEach(function(element) {
|
||||
var id = element.AAC._;
|
||||
var name = scoalesce(element.CN2, element.CON);
|
||||
if (!id && name === '所属未定') {
|
||||
id = 'pending';
|
||||
}
|
||||
var feature = names[id];
|
||||
if (!feature) {
|
||||
names[id] = feature = {id: id, name: name, curves: []};
|
||||
}
|
||||
surfaces[element.ARE].curves.forEach(function(element) {
|
||||
feature.curves.push(element);
|
||||
});
|
||||
});
|
||||
return names;
|
||||
}
|
||||
|
||||
function isInBounds(point) {
|
||||
var longitude = point[0];
|
||||
var latitude = point[1];
|
||||
return 138.90 < longitude && longitude < 139.95 &&
|
||||
35.45 < latitude && latitude < 35.95;
|
||||
}
|
||||
|
||||
function buildGeometry(curveRefs, curves) {
|
||||
var allowedCurves = [];
|
||||
curveRefs.forEach(function(id) {
|
||||
var points = curves[id].points;
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
if (!isInBounds(points[i])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
allowedCurves.push(id);
|
||||
});
|
||||
|
||||
if (allowedCurves.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var multi = allowedCurves.length > 1;
|
||||
var coordinates = [];
|
||||
allowedCurves.forEach(function(id) {
|
||||
var points = curves[id].points;
|
||||
coordinates.push(multi ? [points] : points);
|
||||
});
|
||||
return multi ?
|
||||
{type: 'MultiPolygon', coordinates: coordinates} :
|
||||
{type: 'Polygon', coordinates: coordinates};
|
||||
}
|
||||
|
||||
function buildFeature(n, curves) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
id: n.id,
|
||||
properties: {name: n.name},
|
||||
geometry: buildGeometry(n.curves, curves)
|
||||
};
|
||||
}
|
||||
|
||||
function buildFeatures(names, curves) {
|
||||
var result = [];
|
||||
_.values(names).forEach(function(element) {
|
||||
var feature = buildFeature(element, curves);
|
||||
if (feature.geometry) {
|
||||
result.push(feature);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
22
ksj/package.json
Normal file
22
ksj/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "ksj",
|
||||
"description": "国土数値情報 Transformer",
|
||||
"version": "0.0.1",
|
||||
"author": {
|
||||
"name": "Cameron Beccario",
|
||||
"email": "cambecc@nullschool.net"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/cambecc/air"
|
||||
},
|
||||
"engine": "node >= 0.10.12",
|
||||
"dependencies": {
|
||||
"underscore": "1.5.1",
|
||||
"when": "2.2.1",
|
||||
"xml2js": "0.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodeunit": "nodeunit-0.8.1"
|
||||
}
|
||||
}
|
||||
2841
public/canvg.js
Executable file
2841
public/canvg.js
Executable file
File diff suppressed because it is too large
Load Diff
8814
public/d3.v3.js
vendored
Normal file
8814
public/d3.v3.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
414
public/index.html
Normal file
414
public/index.html
Normal file
@ -0,0 +1,414 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style>
|
||||
.tk-fill {
|
||||
fill: #303030;
|
||||
}
|
||||
|
||||
.tk-inboundary {
|
||||
fill: none;
|
||||
stroke: #505050;
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: 2, 2;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.tk-outboundary {
|
||||
fill: none;
|
||||
stroke: #808080;
|
||||
stroke-width: 1;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
.station {
|
||||
fill: #4986bc;
|
||||
}
|
||||
|
||||
#pos {
|
||||
fill: #3aff3a;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="map" style="position: absolute;">
|
||||
<svg id="map-svg"></svg>
|
||||
</div>
|
||||
<div id="mask" style="position: absolute; visibility: hidden;">
|
||||
<svg id="mask-svg" style="position: absolute;"></svg>
|
||||
<canvas id="mask-canvas" style="position: absolute;"></canvas>
|
||||
</div>
|
||||
<div id="field" style="position: absolute; z-index: 10;">
|
||||
<canvas id="field-canvas" style="position: absolute;"></canvas>
|
||||
</div>
|
||||
|
||||
<script src="topojson.v1.js"></script>
|
||||
<script src="d3.v3.js"></script>
|
||||
<script src="canvg.js"></script>
|
||||
<script>
|
||||
|
||||
var width = 1200, height = 700;
|
||||
// var width = 600, height = 350;
|
||||
var π = Math.PI;
|
||||
|
||||
var projection = d3.geo.albers()
|
||||
.rotate([-139.3, 0])
|
||||
.center([0, 144.20])
|
||||
.scale(80000);
|
||||
// var projection = d3.geo.albers()
|
||||
// .rotate([-139.75, 0])
|
||||
// .center([0, 144.40])
|
||||
// .scale(40000);
|
||||
|
||||
var path = d3.geo.path().projection(projection);
|
||||
|
||||
var mapSvg = d3.select("#map-svg").attr("width", width).attr("height", height);
|
||||
var maskSvg = d3.select("#mask-svg").attr("width", width).attr("height", height);
|
||||
var maskCanvas = d3.select("#mask-canvas").attr("width", width).attr("height", height)[0][0];
|
||||
var fieldCanvas = d3.select("#field-canvas").attr("width", width).attr("height", height)[0][0];
|
||||
|
||||
d3.json("tk.topojson", function (error, tk) {
|
||||
var datum = topojson.feature(tk, tk.objects.tk);
|
||||
|
||||
mapSvg.selectAll(".tk")
|
||||
.data(datum.features)
|
||||
.enter().append("path")
|
||||
.attr("class", "tk-fill")
|
||||
.attr("d", path);
|
||||
|
||||
mapSvg.append("path")
|
||||
.datum(topojson.mesh(tk, tk.objects.tk, function (a, b) { return a !== b; }))
|
||||
.attr("class", "tk-inboundary")
|
||||
.attr("d", path);
|
||||
|
||||
mapSvg.append("path")
|
||||
.datum(topojson.mesh(tk, tk.objects.tk, function(a, b) { return a === b; }))
|
||||
.attr("class", "tk-outboundary")
|
||||
.attr("d", path);
|
||||
|
||||
maskSvg.append("path")
|
||||
.datum(topojson.mesh(tk, tk.objects.tk, function(a, b) { return a === b; }))
|
||||
.attr("fill", "#fff")
|
||||
.attr("stroke-width", "50")
|
||||
.attr("stroke", "#fff")
|
||||
.attr("stroke-linejoin", "round")
|
||||
.attr("d", path);
|
||||
|
||||
canvg(maskCanvas, document.getElementById("mask").innerHTML.trim());
|
||||
var maskCanvasContext = maskCanvas.getContext("2d");
|
||||
var maskData = maskCanvasContext.getImageData(0, 0, maskCanvas.width, maskCanvas.height).data;
|
||||
|
||||
for (var x = maskCanvas.width - 1; x >= 0; x -= 1) {
|
||||
var column = fieldMask[x] = [];
|
||||
for (var y = maskCanvas.height - 1; y >= 0; y -= 1) {
|
||||
var i = (y * maskCanvas.width + x) * 4;
|
||||
column[y] = maskData[i] > 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(function(position) {
|
||||
var p = projection([position.coords.longitude, position.coords.latitude]);
|
||||
var x = Math.round(p[0]);
|
||||
var y = Math.round(p[1]);
|
||||
if (0 <= x && x < width && 0 <= y && y < height) {
|
||||
mapSvg.append("circle")
|
||||
.attr("cx", x)
|
||||
.attr("cy", y)
|
||||
.attr("r", 3)
|
||||
.attr("id", "pos");
|
||||
}
|
||||
},
|
||||
console.error.bind(console),
|
||||
{enableHighAccuracy: true});
|
||||
}
|
||||
|
||||
d3.json("stations/geo", function(error, stations) {
|
||||
path.pointRadius(1);
|
||||
mapSvg.append("path")
|
||||
.datum(stations)
|
||||
.attr("class", "station")
|
||||
.attr("d", path);
|
||||
|
||||
interpolateVectorField();
|
||||
// interpolateScalarField('no2');
|
||||
});
|
||||
});
|
||||
|
||||
var done = false;
|
||||
|
||||
var fieldMask = [];
|
||||
|
||||
d3.select("#field-canvas").on("click", printCoord);
|
||||
|
||||
function printCoord() {
|
||||
console.log(d3.mouse(this));
|
||||
console.log(projection.invert(d3.mouse(this)));
|
||||
done = true;
|
||||
}
|
||||
|
||||
var particles = [];
|
||||
var maxAge = 150;
|
||||
|
||||
function distance(x1, y1, x2, y2) {
|
||||
var xd = Math.abs(x1 - x2);
|
||||
var yd = Math.abs(y1 - y2);
|
||||
return Math.sqrt(xd * xd + yd * yd);
|
||||
}
|
||||
|
||||
function weight(x1, y1, x2, y2) {
|
||||
var d = distance(x1, y1, x2, y2);
|
||||
return 1 / (d * d);
|
||||
}
|
||||
|
||||
function multiply(x, y) {
|
||||
return x * y;
|
||||
}
|
||||
|
||||
function add(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function vectorScale(v, m) {
|
||||
return [v[0], v[1] * m];
|
||||
}
|
||||
|
||||
function vectorAdd(a, b) {
|
||||
var ax = Math.cos(a[0]) * a[1];
|
||||
var ay = Math.sin(a[0]) * a[1];
|
||||
var bx = Math.cos(b[0]) * b[1];
|
||||
var by = Math.sin(b[0]) * b[1];
|
||||
|
||||
var cx = ax + bx;
|
||||
var cy = ay + by;
|
||||
|
||||
var r = Math.atan2(cy, cx);
|
||||
var m = Math.sqrt(cx * cx + cy * cy);
|
||||
|
||||
if (!isFinite(r)) {
|
||||
r = 0;
|
||||
}
|
||||
return [r, m];
|
||||
}
|
||||
|
||||
function f(x, y, initial, data, scale, add) {
|
||||
var n = initial;
|
||||
var d = 0;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var sample = data[i];
|
||||
var value = sample[2];
|
||||
var w = weight(x, y, sample[0], sample[1]);
|
||||
if (w === Number.POSITIVE_INFINITY) {
|
||||
return value;
|
||||
}
|
||||
var s = scale(value, w);
|
||||
n = add(n, s);
|
||||
d += w;
|
||||
}
|
||||
return scale(n, 1 / d);
|
||||
}
|
||||
|
||||
var c = fieldCanvas;
|
||||
var g = c.getContext('2d');
|
||||
|
||||
function interpolateScalarField(sampleType) {
|
||||
// d3.json("samples/2013/8/23/11", function(error, samples) {
|
||||
d3.json("samples/current", function(error, samples) {
|
||||
var values = [];
|
||||
samples.forEach(function(sample) {
|
||||
if (sample[sampleType]) {
|
||||
values.push([sample.longitude * 1, sample.latitude * 1, sample[sampleType] * 1]);
|
||||
}
|
||||
});
|
||||
var field = [];
|
||||
var min = Number.POSITIVE_INFINITY;
|
||||
var max = Number.NEGATIVE_INFINITY;
|
||||
for (var x = width; x >= 350; x--) {
|
||||
field[x] = [];
|
||||
for (var y = height; y >= 150; y--) {
|
||||
var p = projection.invert([x, y]);
|
||||
var v = f(p[0], p[1], 0, values, multiply, add);
|
||||
field[x][y] = v;
|
||||
if (v < min) {
|
||||
min = v;
|
||||
}
|
||||
if (v > max) {
|
||||
max = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
processScalarField(field, min, max);
|
||||
});
|
||||
|
||||
function processScalarField(field, min, max) {
|
||||
var styles = [];
|
||||
for (var i = 0; i < 255; i += 1) {
|
||||
styles.push('rgba(' + i + ', ' + i + ', ' + i + ', 0.6)');
|
||||
}
|
||||
var range = max - min;
|
||||
|
||||
for (var x = 350; x < width; x+=1) {
|
||||
for (var y = 150; y < height; y+=1) {
|
||||
if (fieldMask[x][y]) {
|
||||
var v = field[x][y];
|
||||
var style = styles[Math.floor((v-min)/range * (styles.length-1))];
|
||||
g.fillStyle = style;
|
||||
g.fillRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function interpolateVectorField() {
|
||||
|
||||
// 2013年8月24日16時
|
||||
// 2013年8月22日19時
|
||||
// 2013年8月21日22時
|
||||
// 2013年8月21日17時
|
||||
// 2013年8月21日16時
|
||||
// 2013年8月21日15時
|
||||
// 2013年8月20日22時
|
||||
// 2013年8月20日21時 -- and each preceding hour...
|
||||
// 2013年8月19日16時
|
||||
// 2013年8月18日17時
|
||||
|
||||
// d3.json("samples/2013/8/24/16", function(error, samples) {
|
||||
// d3.json("samples/2013/8/22/19", function(error, samples) {
|
||||
// d3.json("samples/2013/8/21/22", function(error, samples) {
|
||||
// d3.json("samples/2013/8/21/16", function(error, samples) {
|
||||
// d3.json("samples/2013/8/21/15", function(error, samples) {
|
||||
// d3.json("samples/2013/8/20/22", function(error, samples) {
|
||||
// d3.json("samples/2013/8/20/21", function(error, samples) {
|
||||
// d3.json("samples/2013/8/20/20", function(error, samples) {
|
||||
// d3.json("samples/2013/8/20/19", function(error, samples) {
|
||||
// d3.json("samples/2013/8/20/18", function(error, samples) {
|
||||
// d3.json("samples/2013/8/19/16", function(error, samples) {
|
||||
// d3.json("samples/2013/8/18/17", function(error, samples) {
|
||||
// d3.json("samples/2013/8/17/17", function(error, samples) {
|
||||
d3.json("samples/2013/8/16/15", function(error, samples) {
|
||||
// d3.json("samples/2013/8/12/19", function(error, samples) {
|
||||
// d3.json("samples/current", function(error, samples) {
|
||||
// Convert cardinal (north origin, clockwise) to radians (counter-clockwise)
|
||||
|
||||
var vectors = [];
|
||||
samples.forEach(function(sample) {
|
||||
if (sample.wd && sample.wv) {
|
||||
var r = sample.wd / 180 * π;
|
||||
vectors.push([
|
||||
sample.longitude * 1,
|
||||
sample.latitude * 1,
|
||||
[Math.atan2(Math.cos(r), Math.sin(r)), sample.wv * 1]]);
|
||||
}
|
||||
});
|
||||
|
||||
var field = [];
|
||||
for (var x = width-1; x >= 0; x--) {
|
||||
var column = field[x] = [];
|
||||
for (var y = height-1; y >= 0; y--) {
|
||||
if (fieldMask[x][y]) {
|
||||
var p = projection.invert([x, y]);
|
||||
var v = f(p[0], p[1], [0, 0], vectors, vectorScale, vectorAdd);
|
||||
var r = v[0];
|
||||
var m = v[1];
|
||||
column[y] = [Math.cos(r + π) * m, -Math.sin(r + π) * m, m];
|
||||
}
|
||||
}
|
||||
}
|
||||
processVectorField(field);
|
||||
});
|
||||
|
||||
function randomPoint() {
|
||||
var x;
|
||||
var y;
|
||||
do {
|
||||
x = Math.random() * (width - 1);
|
||||
y = Math.random() * (height - 1);
|
||||
} while (!fieldMask[Math.round(x)][Math.round(y)]);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
function processVectorField(field) {
|
||||
for (var i = 0; i < 5000; i++) {
|
||||
var p = randomPoint();
|
||||
particles.push({
|
||||
x: p[0],
|
||||
y: p[1],
|
||||
age: Math.floor(Math.random() * maxAge)
|
||||
});
|
||||
}
|
||||
|
||||
var styles = [];
|
||||
for (var j = 150; j < 255; j += 1) {
|
||||
styles.push('rgba(' + j + ', ' + j + ', ' + j + ', 1)');
|
||||
}
|
||||
var max = 17;
|
||||
var min = 0;
|
||||
var range = max - min;
|
||||
|
||||
draw();
|
||||
|
||||
function draw() {
|
||||
var prev = g.globalCompositeOperation;
|
||||
g.fillStyle = 'rgba(0, 0, 0, 0.8)';
|
||||
g.globalCompositeOperation = "destination-in";
|
||||
g.fillRect(0, 0, c.width, c.height);
|
||||
g.globalCompositeOperation = prev;
|
||||
|
||||
g.lineWidth = 1;
|
||||
|
||||
g.beginPath();
|
||||
particles.forEach(function(particle) {
|
||||
if (particle.age > maxAge) {
|
||||
particle.age = 0;
|
||||
var p = randomPoint();
|
||||
particle.x = p[0];
|
||||
particle.y = p[1];
|
||||
}
|
||||
|
||||
// get vector at current location
|
||||
var x = particle.x;
|
||||
var y = particle.y;
|
||||
var fx = Math.round(x);
|
||||
var fy = Math.round(y);
|
||||
|
||||
if (fx < field.length && field[fx] && fy < field[fx].length && field[fx][fy]) {
|
||||
if (fx < fieldMask.length && fy < fieldMask[fx].length && fieldMask[fx][fy]) {
|
||||
var v = field[fx][fy];
|
||||
var xt = x + v[0] * 0.4;
|
||||
var yt = y + v[1] * 0.4;
|
||||
|
||||
var style = styles[Math.floor((Math.min(v[2], max) - min) / range * (styles.length - 1))];
|
||||
|
||||
// g.fillStyle = style;
|
||||
// g.fillRect(Math.round(xt), Math.round(yt), 1, 1);
|
||||
|
||||
g.strokeStyle = style;
|
||||
g.moveTo(Math.round(x), Math.round(y));
|
||||
g.lineTo(Math.round(xt), Math.round(yt));
|
||||
}
|
||||
particle.x = xt;
|
||||
particle.y = yt;
|
||||
}
|
||||
particle.age += 1;
|
||||
});
|
||||
g.stroke();
|
||||
|
||||
if (!done) {
|
||||
setTimeout(draw, 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
457
public/topojson.v1.js
Normal file
457
public/topojson.v1.js
Normal file
@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Downloaded from http://d3js.org/topojson.v1.js, 2013-08-20 16:37
|
||||
*/
|
||||
|
||||
topojson = (function() {
|
||||
|
||||
function merge(topology, arcs) {
|
||||
var fragmentByStart = {},
|
||||
fragmentByEnd = {};
|
||||
|
||||
arcs.forEach(function(i) {
|
||||
var e = ends(i),
|
||||
start = e[0],
|
||||
end = e[1],
|
||||
f, g;
|
||||
|
||||
if (f = fragmentByEnd[start]) {
|
||||
delete fragmentByEnd[f.end];
|
||||
f.push(i);
|
||||
f.end = end;
|
||||
if (g = fragmentByStart[end]) {
|
||||
delete fragmentByStart[g.start];
|
||||
var fg = g === f ? f : f.concat(g);
|
||||
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
|
||||
} else if (g = fragmentByEnd[end]) {
|
||||
delete fragmentByStart[g.start];
|
||||
delete fragmentByEnd[g.end];
|
||||
var fg = f.concat(g.map(function(i) { return ~i; }).reverse());
|
||||
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.start] = fg;
|
||||
} else {
|
||||
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
|
||||
}
|
||||
} else if (f = fragmentByStart[end]) {
|
||||
delete fragmentByStart[f.start];
|
||||
f.unshift(i);
|
||||
f.start = start;
|
||||
if (g = fragmentByEnd[start]) {
|
||||
delete fragmentByEnd[g.end];
|
||||
var gf = g === f ? f : g.concat(f);
|
||||
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
|
||||
} else if (g = fragmentByStart[start]) {
|
||||
delete fragmentByStart[g.start];
|
||||
delete fragmentByEnd[g.end];
|
||||
var gf = g.map(function(i) { return ~i; }).reverse().concat(f);
|
||||
fragmentByStart[gf.start = g.end] = fragmentByEnd[gf.end = f.end] = gf;
|
||||
} else {
|
||||
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
|
||||
}
|
||||
} else if (f = fragmentByStart[start]) {
|
||||
delete fragmentByStart[f.start];
|
||||
f.unshift(~i);
|
||||
f.start = end;
|
||||
if (g = fragmentByEnd[end]) {
|
||||
delete fragmentByEnd[g.end];
|
||||
var gf = g === f ? f : g.concat(f);
|
||||
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
|
||||
} else if (g = fragmentByStart[end]) {
|
||||
delete fragmentByStart[g.start];
|
||||
delete fragmentByEnd[g.end];
|
||||
var gf = g.map(function(i) { return ~i; }).reverse().concat(f);
|
||||
fragmentByStart[gf.start = g.end] = fragmentByEnd[gf.end = f.end] = gf;
|
||||
} else {
|
||||
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
|
||||
}
|
||||
} else if (f = fragmentByEnd[end]) {
|
||||
delete fragmentByEnd[f.end];
|
||||
f.push(~i);
|
||||
f.end = start;
|
||||
if (g = fragmentByEnd[start]) {
|
||||
delete fragmentByStart[g.start];
|
||||
var fg = g === f ? f : f.concat(g);
|
||||
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
|
||||
} else if (g = fragmentByStart[start]) {
|
||||
delete fragmentByStart[g.start];
|
||||
delete fragmentByEnd[g.end];
|
||||
var fg = f.concat(g.map(function(i) { return ~i; }).reverse());
|
||||
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.start] = fg;
|
||||
} else {
|
||||
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
|
||||
}
|
||||
} else {
|
||||
f = [i];
|
||||
fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
|
||||
}
|
||||
});
|
||||
|
||||
function ends(i) {
|
||||
var arc = topology.arcs[i], p0 = arc[0], p1 = [0, 0];
|
||||
arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
|
||||
return [p0, p1];
|
||||
}
|
||||
|
||||
var fragments = [];
|
||||
for (var k in fragmentByEnd) fragments.push(fragmentByEnd[k]);
|
||||
return fragments;
|
||||
}
|
||||
|
||||
function mesh(topology, o, filter) {
|
||||
var arcs = [];
|
||||
|
||||
if (arguments.length > 1) {
|
||||
var geomsByArc = [],
|
||||
geom;
|
||||
|
||||
function arc(i) {
|
||||
if (i < 0) i = ~i;
|
||||
(geomsByArc[i] || (geomsByArc[i] = [])).push(geom);
|
||||
}
|
||||
|
||||
function line(arcs) {
|
||||
arcs.forEach(arc);
|
||||
}
|
||||
|
||||
function polygon(arcs) {
|
||||
arcs.forEach(line);
|
||||
}
|
||||
|
||||
function geometry(o) {
|
||||
if (o.type === "GeometryCollection") o.geometries.forEach(geometry);
|
||||
else if (o.type in geometryType) {
|
||||
geom = o;
|
||||
geometryType[o.type](o.arcs);
|
||||
}
|
||||
}
|
||||
|
||||
var geometryType = {
|
||||
LineString: line,
|
||||
MultiLineString: polygon,
|
||||
Polygon: polygon,
|
||||
MultiPolygon: function(arcs) { arcs.forEach(polygon); }
|
||||
};
|
||||
|
||||
geometry(o);
|
||||
|
||||
geomsByArc.forEach(arguments.length < 3
|
||||
? function(geoms, i) { arcs.push(i); }
|
||||
: function(geoms, i) { if (filter(geoms[0], geoms[geoms.length - 1])) arcs.push(i); });
|
||||
} else {
|
||||
for (var i = 0, n = topology.arcs.length; i < n; ++i) arcs.push(i);
|
||||
}
|
||||
|
||||
return object(topology, {type: "MultiLineString", arcs: merge(topology, arcs)});
|
||||
}
|
||||
|
||||
function featureOrCollection(topology, o) {
|
||||
return o.type === "GeometryCollection" ? {
|
||||
type: "FeatureCollection",
|
||||
features: o.geometries.map(function(o) { return feature(topology, o); })
|
||||
} : feature(topology, o);
|
||||
}
|
||||
|
||||
function feature(topology, o) {
|
||||
var f = {
|
||||
type: "Feature",
|
||||
id: o.id,
|
||||
properties: o.properties || {},
|
||||
geometry: object(topology, o)
|
||||
};
|
||||
if (o.id == null) delete f.id;
|
||||
return f;
|
||||
}
|
||||
|
||||
function object(topology, o) {
|
||||
var tf = topology.transform,
|
||||
kx = tf.scale[0],
|
||||
ky = tf.scale[1],
|
||||
dx = tf.translate[0],
|
||||
dy = tf.translate[1],
|
||||
arcs = topology.arcs;
|
||||
|
||||
function arc(i, points) {
|
||||
if (points.length) points.pop();
|
||||
for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length, x = 0, y = 0, p; k < n; ++k) {
|
||||
points.push(p = a[k].slice());
|
||||
p[0] = (x += p[0]) * kx + dx;
|
||||
p[1] = (y += p[1]) * ky + dy;
|
||||
}
|
||||
if (i < 0) reverse(points, n);
|
||||
}
|
||||
|
||||
function point(p) {
|
||||
p = p.slice();
|
||||
p[0] = p[0] * kx + dx;
|
||||
p[1] = p[1] * ky + dy;
|
||||
return p;
|
||||
}
|
||||
|
||||
function line(arcs) {
|
||||
var points = [];
|
||||
for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
|
||||
if (points.length < 2) points.push(points[0].slice());
|
||||
return points;
|
||||
}
|
||||
|
||||
function ring(arcs) {
|
||||
var points = line(arcs);
|
||||
while (points.length < 4) points.push(points[0].slice());
|
||||
return points;
|
||||
}
|
||||
|
||||
function polygon(arcs) {
|
||||
return arcs.map(ring);
|
||||
}
|
||||
|
||||
function geometry(o) {
|
||||
var t = o.type;
|
||||
return t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)}
|
||||
: t in geometryType ? {type: t, coordinates: geometryType[t](o)}
|
||||
: null;
|
||||
}
|
||||
|
||||
var geometryType = {
|
||||
Point: function(o) { return point(o.coordinates); },
|
||||
MultiPoint: function(o) { return o.coordinates.map(point); },
|
||||
LineString: function(o) { return line(o.arcs); },
|
||||
MultiLineString: function(o) { return o.arcs.map(line); },
|
||||
Polygon: function(o) { return polygon(o.arcs); },
|
||||
MultiPolygon: function(o) { return o.arcs.map(polygon); }
|
||||
};
|
||||
|
||||
return geometry(o);
|
||||
}
|
||||
|
||||
function reverse(array, n) {
|
||||
var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
|
||||
}
|
||||
|
||||
function bisect(a, x) {
|
||||
var lo = 0, hi = a.length;
|
||||
while (lo < hi) {
|
||||
var mid = lo + hi >>> 1;
|
||||
if (a[mid] < x) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
function neighbors(objects) {
|
||||
var indexesByArc = {}, // arc index -> array of object indexes
|
||||
neighbors = objects.map(function() { return []; });
|
||||
|
||||
function line(arcs, i) {
|
||||
arcs.forEach(function(a) {
|
||||
if (a < 0) a = ~a;
|
||||
var o = indexesByArc[a];
|
||||
if (o) o.push(i);
|
||||
else indexesByArc[a] = [i];
|
||||
});
|
||||
}
|
||||
|
||||
function polygon(arcs, i) {
|
||||
arcs.forEach(function(arc) { line(arc, i); });
|
||||
}
|
||||
|
||||
function geometry(o, i) {
|
||||
if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); });
|
||||
else if (o.type in geometryType) geometryType[o.type](o.arcs, i);
|
||||
}
|
||||
|
||||
var geometryType = {
|
||||
LineString: line,
|
||||
MultiLineString: polygon,
|
||||
Polygon: polygon,
|
||||
MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); }
|
||||
};
|
||||
|
||||
objects.forEach(geometry);
|
||||
|
||||
for (var i in indexesByArc) {
|
||||
for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) {
|
||||
for (var k = j + 1; k < m; ++k) {
|
||||
var ij = indexes[j], ik = indexes[k], n;
|
||||
if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik);
|
||||
if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
function presimplify(topology, triangleArea) {
|
||||
var heap = minHeap(compareArea),
|
||||
maxArea = 0,
|
||||
triangle;
|
||||
|
||||
if (!triangleArea) triangleArea = cartesianArea;
|
||||
|
||||
topology.arcs.forEach(function(arc) {
|
||||
var triangles = [];
|
||||
|
||||
arc.forEach(transformAbsolute(topology.transform));
|
||||
|
||||
for (var i = 1, n = arc.length - 1; i < n; ++i) {
|
||||
triangle = arc.slice(i - 1, i + 2);
|
||||
triangle[1][2] = triangleArea(triangle);
|
||||
triangles.push(triangle);
|
||||
heap.push(triangle);
|
||||
}
|
||||
|
||||
// Always keep the arc endpoints!
|
||||
arc[0][2] = arc[n][2] = Infinity;
|
||||
|
||||
for (var i = 0, n = triangles.length; i < n; ++i) {
|
||||
triangle = triangles[i];
|
||||
triangle.previous = triangles[i - 1];
|
||||
triangle.next = triangles[i + 1];
|
||||
}
|
||||
});
|
||||
|
||||
while (triangle = heap.pop()) {
|
||||
var previous = triangle.previous,
|
||||
next = triangle.next;
|
||||
|
||||
// If the area of the current point is less than that of the previous point
|
||||
// to be eliminated, use the latter's area instead. This ensures that the
|
||||
// current point cannot be eliminated without eliminating previously-
|
||||
// eliminated points.
|
||||
if (triangle[1][2] < maxArea) triangle[1][2] = maxArea;
|
||||
else maxArea = triangle[1][2];
|
||||
|
||||
if (previous) {
|
||||
previous.next = next;
|
||||
previous[2] = triangle[2];
|
||||
update(previous);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
next.previous = previous;
|
||||
next[0] = triangle[0];
|
||||
update(next);
|
||||
}
|
||||
}
|
||||
|
||||
topology.arcs.forEach(function(arc) {
|
||||
arc.forEach(transformRelative(topology.transform));
|
||||
});
|
||||
|
||||
function update(triangle) {
|
||||
heap.remove(triangle);
|
||||
triangle[1][2] = triangleArea(triangle);
|
||||
heap.push(triangle);
|
||||
}
|
||||
|
||||
return topology;
|
||||
};
|
||||
|
||||
function cartesianArea(triangle) {
|
||||
return Math.abs(
|
||||
(triangle[0][0] - triangle[2][0]) * (triangle[1][1] - triangle[0][1])
|
||||
- (triangle[0][0] - triangle[1][0]) * (triangle[2][1] - triangle[0][1])
|
||||
);
|
||||
}
|
||||
|
||||
function compareArea(a, b) {
|
||||
return a[1][2] - b[1][2];
|
||||
}
|
||||
|
||||
function minHeap(compare) {
|
||||
var heap = {},
|
||||
array = [];
|
||||
|
||||
heap.push = function() {
|
||||
for (var i = 0, n = arguments.length; i < n; ++i) {
|
||||
var object = arguments[i];
|
||||
up(object.index = array.push(object) - 1);
|
||||
}
|
||||
return array.length;
|
||||
};
|
||||
|
||||
heap.pop = function() {
|
||||
var removed = array[0],
|
||||
object = array.pop();
|
||||
if (array.length) {
|
||||
array[object.index = 0] = object;
|
||||
down(0);
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
heap.remove = function(removed) {
|
||||
var i = removed.index,
|
||||
object = array.pop();
|
||||
if (i !== array.length) {
|
||||
array[object.index = i] = object;
|
||||
(compare(object, removed) < 0 ? up : down)(i);
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
function up(i) {
|
||||
var object = array[i];
|
||||
while (i > 0) {
|
||||
var up = ((i + 1) >> 1) - 1,
|
||||
parent = array[up];
|
||||
if (compare(object, parent) >= 0) break;
|
||||
array[parent.index = i] = parent;
|
||||
array[object.index = i = up] = object;
|
||||
}
|
||||
}
|
||||
|
||||
function down(i) {
|
||||
var object = array[i];
|
||||
while (true) {
|
||||
var right = (i + 1) << 1,
|
||||
left = right - 1,
|
||||
down = i,
|
||||
child = array[down];
|
||||
if (left < array.length && compare(array[left], child) < 0) child = array[down = left];
|
||||
if (right < array.length && compare(array[right], child) < 0) child = array[down = right];
|
||||
if (down === i) break;
|
||||
array[child.index = i] = child;
|
||||
array[object.index = i = down] = object;
|
||||
}
|
||||
}
|
||||
|
||||
return heap;
|
||||
}
|
||||
|
||||
function transformAbsolute(transform) {
|
||||
var x0 = 0,
|
||||
y0 = 0,
|
||||
kx = transform.scale[0],
|
||||
ky = transform.scale[1],
|
||||
dx = transform.translate[0],
|
||||
dy = transform.translate[1];
|
||||
return function(point) {
|
||||
point[0] = (x0 += point[0]) * kx + dx;
|
||||
point[1] = (y0 += point[1]) * ky + dy;
|
||||
};
|
||||
}
|
||||
|
||||
function transformRelative(transform) {
|
||||
var x0 = 0,
|
||||
y0 = 0,
|
||||
kx = transform.scale[0],
|
||||
ky = transform.scale[1],
|
||||
dx = transform.translate[0],
|
||||
dy = transform.translate[1];
|
||||
return function(point) {
|
||||
var x1 = (point[0] - dx) / kx | 0,
|
||||
y1 = (point[1] - dy) / ky | 0;
|
||||
point[0] = x1 - x0;
|
||||
point[1] = y1 - y0;
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
version: "1.3.0",
|
||||
mesh: mesh,
|
||||
feature: featureOrCollection,
|
||||
neighbors: neighbors,
|
||||
presimplify: presimplify
|
||||
};
|
||||
})();
|
||||
19
server.js
19
server.js
@ -18,7 +18,9 @@ var stationsTable = {
|
||||
columns: [
|
||||
{name: 'id', type: 'INTEGER', modifier: 'NOT NULL', description: 'station id'},
|
||||
{name: 'name', type: 'TEXT', description: 'station name'},
|
||||
{name: 'address', type: 'TEXT', description: 'station location'}
|
||||
{name: 'address', type: 'TEXT', description: 'station location'},
|
||||
{name: 'latitude', type: 'NUMERIC(9, 6)', description: 'latitude'},
|
||||
{name: 'longitude', type: 'NUMERIC(9, 6)', description: 'longitude'}
|
||||
],
|
||||
primary: {name: 'stations_PK', columns: ['id']}
|
||||
}
|
||||
@ -207,7 +209,14 @@ function doStationDetails() {
|
||||
statements.push(db.upsert(stationsTable, {id: stationNames[name], name: name}));
|
||||
});
|
||||
stationsData.forEach(function(station) {
|
||||
statements.push(db.upsert(stationsTable, {id: station[0], name: station[1], address: station[2]}));
|
||||
var row = {
|
||||
id: station[0],
|
||||
name: station[1],
|
||||
address: station[2],
|
||||
latitude: station[3],
|
||||
longitude: station[4]
|
||||
};
|
||||
statements.push(db.upsert(stationsTable, row));
|
||||
});
|
||||
return persist(statements);
|
||||
}
|
||||
@ -241,5 +250,9 @@ function doP160Historical(hours) {
|
||||
start()
|
||||
.then(doP160.bind(undefined, null))
|
||||
.then(doStationDetails)
|
||||
.then(doP160Historical.bind(undefined, 0 /*9 * 24*/)) // up to nine days of historical data available
|
||||
.then(doP160Historical.bind(undefined, 0/*9 * 24*/)) // up to nine days of historical data available
|
||||
.then(null, console.error);
|
||||
|
||||
//setInterval(function update() {
|
||||
// doP160(null).then(null, console.error);
|
||||
//}, 10 * 60 * 1000); // update every ten minutes
|
||||
|
||||
@ -1,84 +1,84 @@
|
||||
[
|
||||
[101, "千代田区神田司町", "千代田区神田司町2-2"],
|
||||
[102, "中央区晴海", "中央区晴海3-6-1"],
|
||||
[103, "港区高輪", "港区高輪1-6"],
|
||||
[104, "国設東京新宿", "新宿区内藤町11"],
|
||||
[105, "文京区本駒込", "文京区本駒込4-35-15"],
|
||||
[106, "江東区大島", "江東区大島3-1-3"],
|
||||
[107, "品川区豊町", "品川区豊町2-1-20"],
|
||||
[108, "目黒区碑文谷", "目黒区碑文谷4-19-25"],
|
||||
[109, "大田区東糀谷", "大田区東糀谷1-21-15"],
|
||||
[110, "世田谷区世田谷", "世田谷区世田谷4-21-27"],
|
||||
[111, "渋谷区字田川町", "渋谷区字田川町1-1"],
|
||||
[112, "中野区若宮", "中野区若宮3-53"],
|
||||
[113, "杉並区久我山", "杉並区久我山5-36-17"],
|
||||
[114, "荒川区南千住", "荒川区南千住1-4-11"],
|
||||
[115, "板橋区本町", "板橋区本町24-1"],
|
||||
[116, "練馬区石神井町", "練馬区石神井町5-24地先"],
|
||||
[117, "練馬区北町", "練馬区北町1-14-11"],
|
||||
[118, "足立区西新井", "足立区西新井6-21-3"],
|
||||
[119, "葛飾区鎌倉", "葛飾区鎌倉2-21-4"],
|
||||
[120, "江戸川区鹿骨", "江戸川区鹿骨1-15-1"],
|
||||
[122, "立川市泉町", "立川市泉町1156-9"],
|
||||
[123, "武蔵野市関前", "武蔵野市関前3-2-20"],
|
||||
[124, "青梅市東青梅", "青梅市東青梅1-11-1"],
|
||||
[125, "府中市宮西町", "府中市宮西町2-24"],
|
||||
[126, "調布市深大寺南町", "調布市深大寺南町4-16-23"],
|
||||
[127, "町田市金森", "町田市金森1-22"],
|
||||
[128, "小金井市本町", "小金井市本町6-6-3"],
|
||||
[129, "小平市小川町", "小平市小川町2-1325"],
|
||||
[130, "西東京市田無町", "西東京市田無町4-15-11"],
|
||||
[131, "福生市本町", "福生市本町5"],
|
||||
[132, "狛江市中和泉", "狛江市中和泉3-4-10"],
|
||||
[133, "東大和市奈良橋", "東大和市奈良橋4-573"],
|
||||
[134, "清瀬市上清戸", "清瀬市上清戸2-6-41"],
|
||||
[135, "多摩市愛宕", "多摩市愛宕1-65-1"],
|
||||
[136, "港区台場", "港区台場1-3-1"],
|
||||
[137, "練馬区練馬", "練馬区練馬2-27-28"],
|
||||
[138, "江戸川区春江町", "江戸川区春江町5-3-3"],
|
||||
[139, "西東京市下保谷", "西東京市下保谷1-4"],
|
||||
[140, "江戸川区南葛西", "江戸川区南葛西1-11-1"],
|
||||
[141, "葛飾区水元公園", "葛飾区水元公園3-2"],
|
||||
[142, "世田谷区成城", "世田谷区成城9-25-1"],
|
||||
[143, "足立区綾瀬", "足立区綾瀬6-23"],
|
||||
[144, "町田市能ケ谷", "町田市能ケ谷7-24-1"],
|
||||
[145, "品川区八潮", "品川区八潮5-11-17"],
|
||||
[146, "八王子市片倉町", "八王子市片倉町553"],
|
||||
[148, "八王子市館町", "八王子市館町1097-66"],
|
||||
[149, "八王子市大楽寺町", "八王子市大楽寺町419-1"],
|
||||
[201, "日比谷交差点", "干代田区日比谷公園1-6"],
|
||||
[206, "明治通り大関横丁", "台東区三ノ輪2-5地先"],
|
||||
[208, "京葉道路亀戸", "江東区亀戸7-42-17"],
|
||||
[209, "三ツ目通り辰巳", "江東区辰巳1-9地先"],
|
||||
[210, "北品川交差点", "品川区北品川3-11-22"],
|
||||
[211, "中原口交差点", "品川区西五反田7-25-1"],
|
||||
[212, "山手通り大坂橋", "目黒区青葉合3-6"],
|
||||
[213, "環七通り柿の木坂", "目黒区柿の木坂1-1-4"],
|
||||
[215, "環七通り松原橋", "大田区中馬込2-17地先"],
|
||||
[216, "玉川通り上馬", "世田谷区上馬4-1-3"],
|
||||
[217, "甲州街道大原", "渋谷区笹塚1-64-19"],
|
||||
[224, "中山道大和町", "板椅区大和町14-12"],
|
||||
[226, "日光街道梅島", "足立区中央本町1-17"],
|
||||
[229, "五日市街道武蔵境", "武蔵野市関前5-21"],
|
||||
[231, "新青梅街道東村山", "東村山市本町1-10先"],
|
||||
[232, "甲州街道国立", "国立市谷保6208"],
|
||||
[234, "環八通り八幡山", "世田谷区粕谷2-19"],
|
||||
[236, "東京環状長岡", "西多摩郡瑞穂町長岡1-10"],
|
||||
[237, "青梅街道柳沢", "西東京市柳沢2-18"],
|
||||
[241, "第一京浜高輪", "港区高輪2-20"],
|
||||
[242, "連雀通り下連雀", "三鷹市下連雀7-15-4"],
|
||||
[243, "北本通り王子", "北区王子5-20先"],
|
||||
[244, "水戸街道東向島", "墨田区東向島1-34-5"],
|
||||
[245, "早稲田通り下井草", "杉並区下井草4-3-29"],
|
||||
[246, "川崎街道百草園", "日野市落川946"],
|
||||
[247, "小金井街道東久留米", "東久留米市中央町6-8-1"],
|
||||
[248, "永代通り新川", "中央区新川1-3-1"],
|
||||
[249, "新目白通り下落合", "新宿区下落合2-2地先"],
|
||||
[250, "環七通り亀有", "葛飾区亀有2-75-1"],
|
||||
[251, "甲州街道八木町", "八王子市八木町8"],
|
||||
[252, "中原街道南千束", "大田区南千束1-33-1"],
|
||||
[254, "春日通り大塚", "文京区大塚3-5-1"],
|
||||
[255, "明治通り西巣鴨", "豊島区西巣鴨2-39-5"],
|
||||
[256, "山手通り東中野", "中野区中央2-18-21"],
|
||||
[257, "環八通り千鳥", "大田区千烏三町目3-31先"]
|
||||
[101, "千代田区神田司町", "千代田区神田司町2-2", 35.692752, 139.768119],
|
||||
[102, "中央区晴海", "中央区晴海3-6-1", 35.656316, 139.779176],
|
||||
[103, "港区高輪", "港区高輪1-6", 35.642334, 139.73536],
|
||||
[104, "国設東京新宿", "新宿区内藤町11", 35.687139, 139.710632],
|
||||
[105, "文京区本駒込", "文京区本駒込4-35-15", 35.73338, 139.755093],
|
||||
[106, "江東区大島", "江東区大島3-1-3", 35.689954, 139.82767],
|
||||
[107, "品川区豊町", "品川区豊町2-1-20", 35.610754, 139.720987],
|
||||
[108, "目黒区碑文谷", "目黒区碑文谷4-19-25", 35.619685, 139.682482],
|
||||
[109, "大田区東糀谷", "大田区東糀谷1-21-15", 35.557402, 139.739667],
|
||||
[110, "世田谷区世田谷", "世田谷区世田谷4-21-27", 35.645974, 139.6529],
|
||||
[111, "渋谷区宇田川町", "渋谷区宇田川町1-1", 35.663934, 139.697942],
|
||||
[112, "中野区若宮", "中野区若宮3-53", 35.719229, 139.641002],
|
||||
[113, "杉並区久我山", "杉並区久我山5-36-17", 35.690483, 139.602224],
|
||||
[114, "荒川区南千住", "荒川区南千住1-4-11", 35.73311, 139.789178],
|
||||
[115, "板橋区本町", "板橋区本町24-1", 35.759573, 139.708153],
|
||||
[116, "練馬区石神井町", "練馬区石神井町5-24地先", 35.737739, 139.599098],
|
||||
[117, "練馬区北町", "練馬区北町1-14-11", 35.76534, 139.664243],
|
||||
[118, "足立区西新井", "足立区西新井6-21-3", 35.780151, 139.776423],
|
||||
[119, "葛飾区鎌倉", "葛飾区鎌倉2-21-4", 35.744475, 139.877474],
|
||||
[120, "江戸川区鹿骨", "江戸川区鹿骨1-15-1", 35.708978, 139.885077],
|
||||
[122, "立川市泉町", "立川市泉町1156-9", 35.714, 139.407887],
|
||||
[123, "武蔵野市関前", "武蔵野市関前3-2-20", 35.71285, 139.556187],
|
||||
[124, "青梅市東青梅", "青梅市東青梅1-11-1", 35.787854, 139.275848],
|
||||
[125, "府中市宮西町", "府中市宮西町2-24", 35.669055, 139.478125],
|
||||
[126, "調布市深大寺南町", "調布市深大寺南町4-16-23", 35.666414, 139.554582],
|
||||
[127, "町田市金森", "町田市金森1-22", 35.535725, 139.450539],
|
||||
[128, "小金井市本町", "小金井市本町6-6-3", 35.699474, 139.503048],
|
||||
[129, "小平市小川町", "小平市小川町2-1325", 35.727776, 139.476591],
|
||||
[130, "西東京市田無町", "西東京市田無町4-15-11", 35.728033, 139.534432],
|
||||
[131, "福生市本町", "福生市本町5", 35.738429, 139.326945],
|
||||
[132, "狛江市中和泉", "狛江市中和泉3-4-10", 35.635155, 139.570445],
|
||||
[133, "東大和市奈良橋", "東大和市奈良橋4-573", 35.751654, 139.425794],
|
||||
[134, "清瀬市上清戸", "清瀬市上清戸2-6-41", 35.779903, 139.521115],
|
||||
[135, "多摩市愛宕", "多摩市愛宕1-65-1", 35.634933, 139.4325],
|
||||
[136, "港区台場", "港区台場1-3-1", 35.632454, 139.778513],
|
||||
[137, "練馬区練馬", "練馬区練馬2-27-28", 35.743023, 139.651975],
|
||||
[138, "江戸川区春江町", "江戸川区春江町5-3-3", 35.681928, 139.877188],
|
||||
[139, "西東京市下保谷", "西東京市下保谷1-4", 35.749968, 139.557974],
|
||||
[140, "江戸川区南葛西", "江戸川区南葛西1-11-1", 35.653665, 139.870805],
|
||||
[141, "葛飾区水元公園", "葛飾区水元公園3-2", 35.786401, 139.869274],
|
||||
[142, "世田谷区成城", "世田谷区成城9-25-1", 35.651673, 139.595416],
|
||||
[143, "足立区綾瀬", "足立区綾瀬6-23", 35.769878, 139.82582],
|
||||
[144, "町田市能ケ谷", "町田市能ケ谷7-24-1", 35.591859, 139.481654],
|
||||
[145, "品川区八潮", "品川区八潮5-11-17", 35.600814, 139.75275],
|
||||
[146, "八王子市片倉町", "八王子市片倉町553", 35.64496, 139.339888],
|
||||
[148, "八王子市館町", "八王子市館町1097-66", 35.627083, 139.28757],
|
||||
[149, "八王子市大楽寺町", "八王子市大楽寺町419-1", 35.671391, 139.293068],
|
||||
[201, "日比谷交差点", "千代田区日比谷公園1-6", 35.674834, 139.755746],
|
||||
[206, "明治通り大関横丁", "台東区三ノ輪2-5地先", 35.729258, 139.793295],
|
||||
[208, "京葉道路亀戸", "江東区亀戸7-42-17", 35.696933, 139.835931],
|
||||
[209, "三ツ目通り辰巳", "江東区辰巳1-9地先", 35.649816, 139.810412],
|
||||
[210, "北品川交差点", "品川区北品川3-11-22", 35.616558, 139.740707],
|
||||
[211, "中原口交差点", "品川区西五反田7-25-1", 35.620474, 139.71911],
|
||||
[212, "山手通り大坂橋", "目黒区青葉台3-6", 35.652349, 139.69063],
|
||||
[213, "環七通り柿の木坂", "目黒区柿の木坂1-1-4", 35.623241, 139.679121],
|
||||
[215, "環七通り松原橋", "大田区中馬込2-17地先", 35.59621, 139.709849],
|
||||
[216, "玉川通り上馬", "世田谷区上馬4-1-3", 35.635343, 139.663983],
|
||||
[217, "甲州街道大原", "渋谷区笹塚1-64-19", 35.672572, 139.661566],
|
||||
[224, "中山道大和町", "板橋区大和町14-12", 35.761, 139.705404],
|
||||
[226, "日光街道梅島", "足立区中央本町1-17", 35.77489, 139.804383],
|
||||
[229, "五日市街道武蔵境", "武蔵野市関前5-21", 35.711178, 139.538903],
|
||||
[231, "新青梅街道東村山", "東村山市本町1-10先", 35.752694, 139.466065],
|
||||
[232, "甲州街道国立", "国立市谷保6208", 35.682151, 139.43226],
|
||||
[234, "環八通り八幡山", "世田谷区粕谷2-19", 35.665816, 139.613039],
|
||||
[236, "東京環状長岡", "西多摩郡瑞穂町長岡1-10", 35.775556, 139.336048],
|
||||
[237, "青梅街道柳沢", "西東京市柳沢2-18", 35.724919, 139.553215],
|
||||
[241, "第一京浜高輪", "港区高輪2-20", 35.637514, 139.739849],
|
||||
[242, "連雀通り下連雀", "三鷹市下連雀7-15-4", 35.692465, 139.560983],
|
||||
[243, "北本通り王子", "北区王子5-20先", 35.766331, 139.735941],
|
||||
[244, "水戸街道東向島", "墨田区東向島1-34-5", 35.720156, 139.815391],
|
||||
[245, "早稲田通り下井草", "杉並区下井草4-3-29", 35.719475, 139.619868],
|
||||
[246, "川崎街道百草園", "日野市落川946", 35.65615, 139.432919],
|
||||
[247, "小金井街道東久留米", "東久留米市中央町6-8-1", 35.753456, 139.515847],
|
||||
[248, "永代通り新川", "中央区新川1-3-1", 35.678908, 139.781678],
|
||||
[249, "新目白通り下落合", "新宿区下落合2-2地先", 35.716683, 139.703896],
|
||||
[250, "環七通り亀有", "葛飾区亀有2-75-1", 35.760885, 139.853105],
|
||||
[251, "甲州街道八木町", "八王子市八木町8", 35.661655, 139.320121],
|
||||
[252, "中原街道南千束", "大田区南千束1-33-1", 35.601843, 139.692894],
|
||||
[254, "春日通り大塚", "文京区大塚3-5-1", 35.719327, 139.734729],
|
||||
[255, "明治通り西巣鴨", "豊島区西巣鴨2-39-5", 35.741904, 139.726237],
|
||||
[256, "山手通り東中野", "中野区中央2-18-21", 35.700383, 139.682466],
|
||||
[257, "環八通り千鳥", "大田区千鳥三丁目3-31先", 35.573147, 139.686532]
|
||||
]
|
||||
|
||||
13
test/test.js
Normal file
13
test/test.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var _ = require('underscore');
|
||||
var util = require('util');
|
||||
var when = require('when');
|
||||
var d3 = require('d3');
|
||||
var topojson = require('topojson');
|
||||
var fs = require('fs');
|
||||
var tool = require('../tool');
|
||||
|
||||
exports.testSomething = function(test) {
|
||||
test.done();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user