geo2topo 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. #!/usr/bin/env node
  2. var fs = require("fs"),
  3. path = require("path"),
  4. readline = require("readline"),
  5. commander = require("commander"),
  6. topojson = require("../");
  7. commander
  8. .version(require("../package.json").version)
  9. .usage("[options] <[name=]file>…")
  10. .description("Converts GeoJSON features to TopoJSON objects.")
  11. .option("-o, --out <file>", "output file name; defaults to “-” for stdout", "-")
  12. .option("-n, --newline-delimited", "accept newline-delimited JSON")
  13. .option("-q, --quantization <count>", "pre-quantization parameter; 0 disables quantization", 0)
  14. .parse(process.argv);
  15. if (commander.args.length < 1) commander.args[0] = "-";
  16. var nullType = {},
  17. nullObject = {type: nullType},
  18. objects = {},
  19. out = (commander.out === "-" ? process.stdout : fs.createWriteStream(commander.out)).on("error", handleEpipe);
  20. Promise.all(commander.args.map(read)).then(write).catch(abort);
  21. function read(specifier) {
  22. var i = specifier.indexOf("="),
  23. file = i >= 0 ? specifier.slice(i + 1) : specifier,
  24. name = i >= 0 ? specifier.slice(0, i) : path.basename(specifier, path.extname(specifier));
  25. if (name in objects) {
  26. console.error();
  27. console.error(" error: object “" + name + "” is not unique");
  28. console.error();
  29. process.exit(1);
  30. }
  31. objects[name] = undefined;
  32. return (commander.newlineDelimited ? readNewlineDelimitedObject : readObject)(file === "-"
  33. ? process.stdin : fs.createReadStream(file))
  34. .then(function(object) { objects[name] = object; });
  35. }
  36. function readNewlineDelimitedObject(stream) {
  37. return new Promise(function(resolve, reject) {
  38. var collection;
  39. readline.createInterface({
  40. input: stream,
  41. output: null
  42. }).on("line", function(line) {
  43. var object = JSON.parse(line);
  44. if (object == null) object = nullObject;
  45. switch (object.type) {
  46. case "Feature": {
  47. if (collection) {
  48. if (collection.type !== "FeatureCollection") {
  49. console.error();
  50. console.error(" error: expected geometry, not Feature");
  51. console.error();
  52. process.exit(1);
  53. }
  54. collection.features.push(object);
  55. } else {
  56. collection = {type: "FeatureCollection", features: [object]};
  57. }
  58. break;
  59. }
  60. case nullType:
  61. case "GeometryCollection":
  62. case "MultiLineString":
  63. case "LineString":
  64. case "Polygon":
  65. case "MultiPolygon":
  66. case "Point":
  67. case "MultiPoint": {
  68. if (collection) {
  69. if (collection.type !== "GeometryCollection") {
  70. console.error();
  71. console.error(" error: expected Feature, not " + object.type);
  72. console.error();
  73. process.exit(1);
  74. }
  75. collection.geometries.push(object);
  76. } else {
  77. collection = {type: "GeometryCollection", geometries: [object]};
  78. }
  79. break;
  80. }
  81. default: {
  82. console.error();
  83. console.error(" error: expected Feature or geometry, not " + object.type);
  84. console.error();
  85. process.exit(1);
  86. break;
  87. }
  88. }
  89. }).on("close", function() {
  90. resolve(collection || {type: "FeatureCollection", features: []});
  91. }).on("error", reject);
  92. });
  93. }
  94. function readObject(stream) {
  95. return new Promise(function(resolve, reject) {
  96. var data = [];
  97. stream
  98. .on("data", function(d) { data.push(d); })
  99. .on("end", function() { resolve(JSON.parse(Buffer.concat(data))); })
  100. .on("error", reject);
  101. });
  102. }
  103. function write() {
  104. out.write(JSON.stringify(topojson.topology(objects, +commander.quantization)));
  105. out[out === process.stdout ? "write" : "end"]("\n");
  106. }
  107. function handleEpipe(error) {
  108. if (error.code === "EPIPE" || error.errno === "EPIPE") {
  109. process.exit(0);
  110. }
  111. }
  112. function abort(error) {
  113. console.error(error.stack);
  114. }