16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | other stuff
30 |
31 |
32 |
33 |
34 |
35 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ofpViewer",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "~1.4.0",
6 | "angular-resource": "~1.4.0",
7 | "angular-route": "~1.4.0",
8 | "angular-cookies": "~1.4.0",
9 | "angular-sanitize": "~1.4.0 ",
10 | "ofp.js": "https://github.com/openfloorplan/ofp.js.git",
11 | "bootstrap": "~3.3.1",
12 | "d3": "~3.5.3",
13 | "jquery": "~2.1.3",
14 | "angular-ui-select2": "~0.0.5",
15 | "select2": "~3.4.8",
16 | "angular-ui-bootstrap": "~0.12.0",
17 | "es5-shim": "~2.0.11",
18 | "json3": "~3.3.2",
19 | "leaflet": "~0.7.3",
20 | "angular-ui": "~0.4.0"
21 | },
22 | "devDependencies": {
23 | "angular-mocks": "~1.4.0",
24 | "angular-scenario": "~1.4.0"
25 | },
26 | "ignore": [
27 | "**/.*",
28 | "node_modules",
29 | "components"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/convertSVG.js:
--------------------------------------------------------------------------------
1 | /****
2 | *
3 | * This script is called from Node.js to process a SVG floorplan into:
4 | * 1) a high-res PNG that will be used as a background image
5 | * 2) a new SVG that will contain only overlay/interactive features:
6 | * a) interior spaces
7 | * b) labels
8 | *
9 | * It uses D3.js to process the SVG DOM and PhantomJS to render PNGs
10 | *
11 | * In the future it can be parameterized and turned into a web service
12 | * in order to dynamically change the background image.
13 | * We may also look into using something like Mapnik or TileStache to
14 | * split the high-res into tiles for improved performance
15 | *
16 | *
17 | * Note: I had to pull down this fork of the phantomjs-node module
18 | * https://github.com/sebv/phantomjs-node
19 | *
20 | */
21 |
22 | 'use strict';
23 |
24 | var PNGPath = '/app/media/png/',
25 | SVGPath = '/app/media/svg/';
26 |
27 |
28 | var ConvertSVG = function (filename) {
29 | this.filename = filename;
30 | };
31 |
32 | ConvertSVG.prototype = {
33 | filename: null,
34 |
35 | /**
36 | * Create PNG
37 | * @param fileSource
38 | * @param fileTarget
39 | * @param width
40 | * @param height
41 | * @param resolutionMultiplier
42 | * @param tileTarget
43 | */
44 | createPNG: function (fileSource, fileTarget, tileTarget, width, height, resolutionMultiplier) {
45 | //setup phantomJS to render PNG
46 | var phantom = require('phantom');
47 | phantom.create(function (ph) {
48 | return ph.createPage(function (page) {
49 | //set page dimensions
50 | var pageWidth = width * resolutionMultiplier,
51 | pageHeight = height * resolutionMultiplier;
52 | page.set('viewportSize', {width: pageWidth, height: pageHeight});
53 | console.log("PNG Dimensions Width:" + pageWidth + " Height:" + pageHeight);
54 |
55 | return page.open(fileSource, function (status) {
56 | if (status !== 'success') {
57 | console.log('Unable to load: ' + fileSource);
58 | ph.exit();
59 | } else {
60 | page.render(fileTarget);
61 | console.log('Render Complete!');
62 | ph.exit();
63 |
64 | var gm = require('gm'),
65 | fs = require('fs'),
66 |
67 | cropHeight = pageHeight / 2,
68 | cropWidth = pageWidth / 2;
69 |
70 |
71 | //upperLeft
72 | gm(fileTarget).crop(cropWidth, cropHeight, 0, 0)
73 | .background('white')
74 | .flatten()
75 | .matteColor('white')
76 | .write(tileTarget + "-bg-ul.jpg", function (err) {
77 | if (!err) {
78 | console.log(' Saved ul tile ');
79 | } else {
80 | console.log(err);
81 | }
82 | });
83 |
84 | //lowerLeft
85 | gm(fileTarget).crop(cropWidth, cropHeight, 0, cropHeight)
86 | .background('white')
87 | .flatten()
88 | .matteColor('white')
89 | .write(tileTarget + "-bg-ll.jpg", function (err) {
90 | if (!err) {
91 | console.log(' Saved ll tile ');
92 | } else {
93 | console.log(err);
94 | }
95 | });
96 |
97 | //upperRight
98 | gm(fileTarget).crop(cropWidth, cropHeight, cropWidth, 0)
99 | .background('white')
100 | .flatten()
101 | .matteColor('white')
102 | .write(tileTarget + "-bg-ur.jpg", function (err) {
103 | if (!err) {
104 | console.log(' Saved ur tile ');
105 | } else {
106 | console.log(err);
107 | }
108 | });
109 |
110 | //lowerRight
111 | gm(fileTarget).crop(cropWidth, cropHeight, cropWidth, cropHeight)
112 | .background('white')
113 | .flatten()
114 | .matteColor('white')
115 | .write(tileTarget + "-bg-lr.jpg", function (err) {
116 | if (!err) {
117 | console.log(' Saved lr tile ');
118 | } else {
119 | console.log(err);
120 | }
121 | });
122 |
123 |
124 |
125 | }
126 | });
127 | });
128 | });
129 | },
130 |
131 | /**
132 | * Write data to file
133 | * @param data
134 | * @param fileTarget
135 | */
136 | writeFile: function (data, fileTarget) {
137 | var fs = require('fs');
138 | //write to file
139 | fs.writeFileSync(fileTarget, data);
140 |
141 | },
142 |
143 | //Not using any of this stuff for now
144 | /*mapValue: function (value, minTo, maxTo, minFrom, maxFrom) {
145 | return minTo + (maxTo - minTo) * ((value - minFrom) / (maxFrom - minFrom));
146 | },
147 |
148 | projectCADToLatLng: function (latlng, minX, minY, maxX, maxY) {
149 | var targetMinX = -10.0,
150 | targetMinY = -5.0,
151 | targetMaxX = 10.0,
152 | targetMaxY = 5.0,
153 |
154 | mappedLat = this.mapValue(latlng.lat, targetMinY, targetMaxY, minY, maxY),
155 | mappedLng = this.mapValue(latlng.lng, targetMinX, targetMaxX, minX, maxX);
156 |
157 | return {lat: mappedLat, lng: mappedLng};
158 |
159 | },
160 |
161 | createGDALTranslateCall: function (viewBox, target) {
162 | //gdal_translate -of GTiff -a_ullr ullat ullon lrlat lelon -a_srs EPSG:4269 input.tif output.tif
163 | // ulx uly lrx lry
164 | var ullaty = Number(viewBox.yMin) + Number(viewBox.height),
165 | ullonx = viewBox.xMin,
166 | lrlaty = viewBox.yMin,
167 | lrlonx = Number(viewBox.xMin) + Number(viewBox.width),
168 |
169 | ul = this.projectCADToLatLng({lat: ullaty, lng: ullonx}, ullonx, lrlaty, lrlonx, ullaty),
170 | lr = this.projectCADToLatLng({lat: lrlaty, lng: lrlonx}, ullonx, lrlaty, lrlonx, ullaty),
171 |
172 | output = "gdal_translate -of GTiff -a_ullr " +
173 | ul.lng.toString() + " " + ul.lat.toString() + " " + lr.lng.toString() + " " + lr.lat.toString() +
174 | " -a_srs EPSG:4326 " + target + " " + target;
175 |
176 | return output;
177 |
178 |
179 | },*/
180 |
181 | /**
182 | * Convert
183 | */
184 | convert: function () {
185 |
186 | global.document = require("jsdom").jsdom("");
187 | global.window = global.document.createWindow();
188 |
189 | var fs = require('fs'),
190 | ofp = require('openfloorplan'),
191 |
192 | source = __dirname + SVGPath + this.filename + '.svg',
193 | backgroundTarget = __dirname + PNGPath + this.filename + '-bg.png',
194 | tileTarget = __dirname + PNGPath + this.filename,
195 | overlayTarget = __dirname + SVGPath + this.filename + '-ol.svg',
196 | resolutionMultiplier = 15,
197 | convertSVG = this; //need a local scope reference;
198 |
199 | console.log("File: " + backgroundTarget);
200 |
201 | /////////////
202 | //Overlay
203 | /////////////
204 | fs.readFile(source, function (err, data) {
205 | if (err) {
206 | throw err;
207 | }
208 |
209 | var fp, viewBox;
210 |
211 | console.log("loaded SVG file");
212 |
213 | //set body as the SVG data
214 | global.document.body.innerHTML = data;
215 | //get the floorplan
216 | fp = new ofp.FloorPlan(global.document.body);
217 |
218 | //flag everything for removal
219 | fp.svg.selectAll('g [type=level]').classed('tmp-removed', true);
220 | //un-flag the stuff we want to keep
221 | fp.spaces.layer.classed('tmp-removed', false);
222 | fp.dimensionAnnotations.layer.classed('tmp-removed', false);
223 |
224 | //remove everything else
225 | fp.svg.selectAll('.tmp-removed').remove();
226 |
227 | convertSVG.writeFile(fp.exportSVG(), overlayTarget);
228 |
229 | });
230 |
231 | //////////////////
232 | //Background
233 | /////////////////
234 | //read the source SVG file
235 | fs.readFile(source, function (err, data) {
236 | if (err) {
237 | throw err;
238 | }
239 |
240 | var fp, viewBox;
241 |
242 | console.log("loaded SVG file");
243 |
244 | //set body as the SVG data
245 | global.document.body.innerHTML = data;
246 | //get the floorplan
247 | fp = new ofp.FloorPlan(global.document.body);
248 |
249 | viewBox = fp.getViewBox();
250 |
251 | fp.dimensionAnnotations.remove();
252 |
253 |
254 |
255 | convertSVG.writeFile(fp.exportSVG(), "temp.svg");
256 |
257 | convertSVG.createPNG("temp.svg", backgroundTarget, tileTarget, viewBox.width, viewBox.height, resolutionMultiplier);
258 |
259 | });
260 |
261 |
262 | }
263 |
264 | };
265 |
266 |
267 |
268 | //run it
269 | new ConvertSVG("fs-hi200-01").convert();
270 |
271 |
272 |
273 |
274 |
275 |
276 |
--------------------------------------------------------------------------------
/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma E2E configuration
2 |
3 | // base path, that will be used to resolve files and exclude
4 | basePath = '';
5 |
6 | // list of files / patterns to load in the browser
7 | files = [
8 | ANGULAR_SCENARIO,
9 | ANGULAR_SCENARIO_ADAPTER,
10 | 'test/e2e/**/*.js'
11 | ];
12 |
13 | // list of files to exclude
14 | exclude = [];
15 |
16 | // test results reporter to use
17 | // possible values: dots || progress || growl
18 | reporters = ['progress'];
19 |
20 | // web server port
21 | port = 8080;
22 |
23 | // cli runner port
24 | runnerPort = 9100;
25 |
26 | // enable / disable colors in the output (reporters and logs)
27 | colors = true;
28 |
29 | // level of logging
30 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
31 | logLevel = LOG_INFO;
32 |
33 | // enable / disable watching file and executing tests whenever any file changes
34 | autoWatch = false;
35 |
36 | // Start these browsers, currently available:
37 | // - Chrome
38 | // - ChromeCanary
39 | // - Firefox
40 | // - Opera
41 | // - Safari (only Mac)
42 | // - PhantomJS
43 | // - IE (only Windows)
44 | browsers = ['Chrome'];
45 |
46 | // If browser does not capture in given timeout [ms], kill it
47 | captureTimeout = 5000;
48 |
49 | // Continuous Integration mode
50 | // if true, it capture browsers, run tests and exit
51 | singleRun = false;
52 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 |
3 | // base path, that will be used to resolve files and exclude
4 | basePath = '';
5 |
6 | // list of files / patterns to load in the browser
7 | files = [
8 | JASMINE,
9 | JASMINE_ADAPTER,
10 | 'app/components/angular/angular.js',
11 | 'app/components/angular-mocks/angular-mocks.js',
12 | 'app/scripts/*.js',
13 | 'app/scripts/**/*.js',
14 | 'test/mock/**/*.js',
15 | 'test/spec/**/*.js'
16 | ];
17 |
18 | // list of files to exclude
19 | exclude = [];
20 |
21 | // test results reporter to use
22 | // possible values: dots || progress || growl
23 | reporters = ['progress'];
24 |
25 | // web server port
26 | port = 8080;
27 |
28 | // cli runner port
29 | runnerPort = 9100;
30 |
31 | // enable / disable colors in the output (reporters and logs)
32 | colors = true;
33 |
34 | // level of logging
35 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
36 | logLevel = LOG_INFO;
37 |
38 | // enable / disable watching file and executing tests whenever any file changes
39 | autoWatch = false;
40 |
41 | // Start these browsers, currently available:
42 | // - Chrome
43 | // - ChromeCanary
44 | // - Firefox
45 | // - Opera
46 | // - Safari (only Mac)
47 | // - PhantomJS
48 | // - IE (only Windows)
49 | browsers = ['Chrome'];
50 |
51 | // If browser does not capture in given timeout [ms], kill it
52 | captureTimeout = 5000;
53 |
54 | // Continuous Integration mode
55 | // if true, it capture browsers, run tests and exit
56 | singleRun = false;
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "SYNCADD Systems, Inc.",
3 | "name": "ofp-viewer",
4 | "version": "0.0.1",
5 | "dependencies": {
6 | "compass": "^0.1.0",
7 | "d3": "3.5.3",
8 | "gm": "1.8.1",
9 | "jsdom": "0.3.4",
10 | "ofp.js": "git://github.com/openfloorplan/ofp.js.git",
11 | "phantom": "0.3.5"
12 | },
13 | "devDependencies": {
14 | "grunt": "~0.4.1",
15 | "grunt-contrib-copy": "~0.4.0",
16 | "grunt-contrib-concat": "~0.1.3",
17 | "grunt-contrib-coffee": "~0.6.4",
18 | "grunt-contrib-uglify": "~0.2.0",
19 | "grunt-contrib-compass": "~0.1.3",
20 | "grunt-contrib-jshint": "~0.3.0",
21 | "grunt-contrib-cssmin": "~0.5.0",
22 | "grunt-contrib-connect": "~0.2.0",
23 | "grunt-contrib-clean": "~0.4.0",
24 | "grunt-contrib-htmlmin": "~0.1.1",
25 | "grunt-contrib-imagemin": "~0.1.2",
26 | "grunt-contrib-livereload": "~0.1.2",
27 | "grunt-bower-requirejs": "~0.4.1",
28 | "grunt-usemin": "~0.1.10",
29 | "grunt-regarde": "~0.1.1",
30 | "grunt-rev": "~0.1.0",
31 | "grunt-karma": "~0.3.0",
32 | "grunt-open": "~0.2.0",
33 | "matchdep": "~0.1.1",
34 | "grunt-google-cdn": "~0.1.1",
35 | "grunt-ngmin": "~0.0.2"
36 | },
37 | "engines": {
38 | "node": ">=0.8.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/spec/controllers/floorplan.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: FloorplanCtrl', function() {
4 |
5 | // load the controller's module
6 | beforeEach(module('floorplanApp'));
7 |
8 | var FloorplanCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function($controller) {
13 | scope = {};
14 | FloorplanCtrl = $controller('FloorplanCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function() {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function() {
4 |
5 | // load the controller's module
6 | beforeEach(module('floorplanApp'));
7 |
8 | var MainCtrl,
9 | FloorplanCtrl,
10 | scope;
11 |
12 | // Initialize the controller and a mock scope
13 | beforeEach(inject(function ($controller, $rootScope) {
14 | scope = $rootScope.$new();
15 | MainCtrl = $controller('MainCtrl', {
16 | $scope: scope
17 | });
18 | FloorplanCtrl = $controller('FloorplanCtrl', {
19 | $scope: scope
20 | });
21 | }));
22 |
23 | it('should attach a list of awesomeThings to the scope', function () {
24 | expect(scope.awesomeThings.length).toBe(3);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------