1 | var 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 |
|
20 | function 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 |
|
40 | util.inherits(MapSlicer, events.EventEmitter);
|
41 |
|
42 | MapSlicer.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")
|
70 | }
|
71 | }
|
72 |
|
73 | this.setup();
|
74 | };
|
75 |
|
76 | MapSlicer.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 |
|
93 | MapSlicer.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 |
|
106 | MapSlicer.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 |
|
119 | MapSlicer.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 |
|
144 | MapSlicer.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 |
|
154 | module.exports = MapSlicer; |
\ | No newline at end of file |