UNPKG

5.14 kBJavaScriptView Raw
1var fs = require("fs"),
2 path = require("path"),
3 events = require("events"),
4 async = require("async"),
5 mkdirp = require("mkdirp"),
6 crypto = require("crypto"),
7 util = require("util"),
8 calculateLevelData = require("./util/calculateLevelData"),
9 calculateZoomLevel = require("./util/calculateZoomLevel"),
10 isPowerOfTwo = require("./util/isPowerOfTwo"),
11 flattenTiles = require("./util/flattenTiles"),
12 processLevel = require("./processLevel"),
13 processTile = require("./processTile"),
14 settingsSchema = require("./settingsSchema"),
15 sha1 = crypto.createHash('sha1');
16
17/**
18 *
19 */
20function MapSlicer(options) {
21
22 if (!(this instanceof MapSlicer)) {
23 return new MapSlicer(options);
24 }
25
26 var opts = settingsSchema.validate(options);
27
28 if (opts.error) {
29 this.emit("error", opts.error);
30 return;
31 }
32
33 this.options = opts.value;
34
35 if (options.autoStart) {
36 process.nextTick(this.start.bind(this));
37 }
38}
39
40util.inherits(MapSlicer, events.EventEmitter);
41
42MapSlicer.prototype.start = function start() {
43 var options = this.options;
44
45 if (!options.file) {
46 this.emit("error", new Error("Error#5: Target file required."));
47 return;
48 }
49
50 if (!isPowerOfTwo(options.tileSize)) {
51 this.emit("error", new Error("Error#6: TileSize is not power of 2 like: 128, 256, etc."));
52 return;
53 }
54
55 if (options.outputFolder) {
56 if (options.output) {
57 this.emit("error", new Error("Error#7: Both outputFolder and output are defined. Please define only one."));
58 return;
59 }
60 options.output = path.join(path.dirname(options.file), options.outputFolder, "{z}", "{y}", "{x}.jpg");
61 } else if (!options.output) {
62 options.output = path.join(path.dirname(options.file), path.basename(options.file, path.extname(options.file)), "{z}", "{y}", "{x}.jpg");
63 }
64
65 if (!options.gm) {
66 if (options.imageMagick) {
67 options.gm = require("gm").subClass({ imageMagick: true });
68 } else {
69 options.gm = require("gm") // gm might be replaced to use imagemagick or graphicsMagick
70 }
71 }
72
73 this.setup();
74};
75
76MapSlicer.prototype.setup = function MapSlicer_setup() {
77 fs.exists(this.options.file, function(exists) {
78 if (exists) {
79 this.options.gm(this.options.file).size(function(err, inputSpec) {
80 if (err) {
81 this.emit("error", new Error("Error#2: Error while fetching size of File: " + this.options.file + "; Error: " + err));
82 } else {
83 this.emit("inputSize", inputSpec.width, inputSpec.height);
84 this.startProcess(inputSpec.width, inputSpec.height);
85 }
86 }.bind(this));
87 } else {
88 this.emit("error", new Error("Error#1: File not found: "+this.options.file));
89 }
90 }.bind(this));
91};
92
93MapSlicer.prototype.startProcess = function MapSlicer_startProcess(inputWidth, inputHeight) {
94
95 this.options = calculateZoomLevel(inputWidth, inputHeight, this.options);
96
97 var tasks = this.collectTasks(inputWidth, inputHeight);
98
99 this.emit("start", this.totalTasks, this.options);
100 this.emit("progress", 0, this.totalTasks, this.executedTasks);
101 async.series(tasks, function(err) {
102 this.emit("end");
103 }.bind(this));
104};
105
106MapSlicer.prototype.wrapProgressTask = function MapSlicer_wrapProgressTask(task) {
107 this.totalTasks++;
108 return function(next) {
109 task(function(error, result) {
110 this.executedTasks++;
111 this.emit("progress", this.executedTasks/this.totalTasks, this.totalTasks, this.executedTasks, result);
112 if(error)
113 this.emit("error", error);
114 next(error, result);
115 }.bind(this));
116 }.bind(this);
117};
118
119MapSlicer.prototype.collectTasks = function MapSlicer_collectTasks(imageWidth, imageHeight) {
120 var levels = calculateLevelData(imageWidth, imageHeight, this.options),
121 tasks = [],
122 patternMd5 = sha1.update(this.options.output).digest("hex");
123
124 this.emit("levels", levels);
125
126 this.totalTasks = 0;
127 this.executedTasks = 0;
128 for (var i=0; i<levels.length; ++i) {
129 var level = levels[i],
130 levelTasks = [],
131 tiles = flattenTiles(level),
132 levelFile = path.join(this.options.tmp, patternMd5+"_"+level.level+".sgi");
133
134 tasks.push(this.wrapProgressTask(processLevel(this.options, level, levelFile)));
135 for(var j=0; j<tiles.length; ++j) {
136 var tile = tiles[j];
137 levelTasks.push(this.wrapProgressTask(processTile(this.options, level, levelFile, tile)));
138 }
139 tasks.push(this.makeParallel(levelTasks));
140 }
141 return tasks;
142};
143
144MapSlicer.prototype.makeParallel = function MapSlicer_makeParallel(tasks) {
145 return function(next) {
146 if(this.options.parallelLimit == 0) {
147 async.parallel(tasks, next);
148 } else {
149 async.parallelLimit(tasks, this.options.parallelLimit, next);
150 }
151 }.bind(this);
152};
153
154module.exports = MapSlicer;
\No newline at end of file