├── .gitignore
├── README.md
├── basic-maps
└── layer-selector
│ └── index.html
├── double-map-alt
├── aww-rats.html
├── css
│ ├── dark-theme.css
│ ├── light-theme.css
│ └── makeitresponsive.css
└── index.html
├── intensity-time
└── index.html
├── live-map
└── index.html
├── manhattanhenge
├── city
│ ├── chicago.html
│ ├── dc.html
│ ├── nyc.html
│ └── sf.html
├── img
│ ├── bk.png
│ ├── chicago.png
│ ├── dc.png
│ ├── nyc.png
│ ├── nyc_districts.png
│ ├── pt3.png
│ ├── sf.png
│ └── veknik_osm_london.png
├── index.html
├── js
│ ├── app.js
│ ├── carto.js
│ ├── cartodb.provider.js
│ ├── cartodb.sql.js
│ ├── core.js
│ ├── geojson.provider.js
│ ├── geometry.js
│ ├── mercator.js
│ ├── model.js
│ ├── projector.worker.js
│ ├── renderer.js
│ ├── settings.js
│ ├── shader.js
│ ├── vecnik.leatlet.js
│ └── vecnik.modestmaps.js
├── libs
│ ├── carto.js
│ ├── modestmaps.js
│ ├── suncalc.js
│ └── underscore.js
├── src
│ ├── core.js
│ ├── geo.js
│ ├── latlng.js
│ ├── map.js
│ ├── math.js
│ ├── mercator.js
│ ├── renderer.canvas.js
│ ├── shader.js
│ ├── tween.js
│ └── vec2.js
├── today.html
└── year.html
├── point-clustering
└── index.html
├── private-maps
├── index.html
└── style.css
├── scroll-story
├── basic
│ ├── css
│ │ ├── dark-theme.css
│ │ ├── light-theme.css
│ │ └── makeitresponsive.css
│ ├── index.html
│ └── js
│ │ ├── VideoOverlay.js
│ │ ├── app copy 2.js
│ │ └── app.js
└── pluto
│ ├── css
│ ├── makeitresponsive.css
│ └── pluto-theme.css
│ └── index.html
├── security-definer
├── README.md
├── row-level
│ ├── MultiTable.md
│ ├── README.md
│ └── examples
│ │ ├── multi-table-security.html
│ │ └── row-level-security.html
└── table-level
│ └── README.md
├── smart-markers
├── icon-background.png
└── index.html
├── tornado
├── README.md
├── index.html
└── torque
│ ├── css
│ ├── leaflet.css
│ ├── leaflet.ie.css
│ └── style.css
│ ├── index.html
│ ├── js
│ ├── app.js
│ ├── canvas_layer.js
│ ├── leaflet_tileloader_mixin.js
│ ├── map.js
│ ├── particles.js
│ ├── probs_density_layer.js
│ ├── sprites.js
│ ├── street_density.js
│ ├── time_layer.js
│ ├── torque-hm-worker.js
│ ├── util.js
│ └── vendor
│ │ ├── dat.gui.min.js
│ │ └── leaflet.js
│ ├── scss
│ └── style.scss
│ └── tests
│ ├── d3.min.js
│ └── index.html
├── tree-graph
└── index.html
├── videomap
├── html5
│ └── index.html
├── inset
│ ├── CanvasOverlay.js
│ └── index.html
├── video-inset
│ ├── VideoOverlay.js
│ └── index.html
└── vimeo
│ └── index.html
└── writable
├── function.sql
├── index.html
└── polygon-size.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## CartoDB Examples
2 |
3 | Contact [@andrewxhill](http://twitter.com/andrewxhill)
4 |
5 | #### CartoDB maps with slide layout
6 |
7 | These are maps I created to highlight the PLUTO data shortly after its release by the NYC government. It ended up being the first of my experiments to get a ton of press: [Gizmodo](http://gizmodo.com/see-nyc-from-a-new-angle-with-these-awesomely-nerdy-map-1093545954), [Visual.ly Blog](http://blog.visual.ly/visualizing-nycs-mappluto-database/), [AnimalNewYork](http://animalnewyork.com/2013/this-nyc-open-data-map-is-mind-bogglingly-comprehensive/), [Gothamist](http://gothamist.com/2013/08/10/geek_out_with_awesome_digital_maps.php), [Wired Maps Lab](http://www.wired.com/wiredscience/2013/08/nyc-pluto-data-map-party/), [Curbed](http://ny.curbed.com/archives/2013/08/09/boring_new_york_city_tax_data_makes_for_nonboring_maps.php), [Brokelyn](http://brokelyn.com/new-project-takes-boring-tax-data-and-turns-it-into-cool-maps-about-nyc/), and [Atlantic Cities](http://www.theatlanticcities.com/technology/2013/08/visual-proof-geographic-data-really-should-be-free/6529/)
8 |
9 | - [demo](http://andrewxhill.github.io/cartodb-examples/scroll-story/pluto/index.html)
10 |
11 | #### Scrolling stories with CartoDB templates and multiple maps
12 |
13 | A summary of work I did at the NYPL historical map data hackathon. I just used a scrolling story layout to talk about the maps I created over an afternoon.
14 |
15 | - [demo](http://andrewxhill.github.io/cartodb-examples/scroll-story/basic/index.html)
16 |
17 | Yea! This got a little love from [Wired](http://www.wired.com/wiredscience/2013/10/phone-map-game-new-york-city/)
18 |
19 | #### Sun position plus street orientation
20 |
21 | Demo I put together of the VECNIK vector rendering with current sunposition to find streets getting a lot of sun exposure
22 |
23 | - [demo](http://andrewxhill.github.io/cartodb-examples/manhattanhenge/today.html)
24 |
25 | or taking a look at places where Manhattanhenge happens for any day of the year,
26 |
27 | - [demo](http://andrewxhill.github.io/cartodb-examples/manhattanhenge/year.html)
28 |
29 | see a couple of other cities,
30 |
31 | - [demo](http://andrewxhill.github.io/cartodb-examples/manhattanhenge/index.html)
32 |
33 | This was built into a full [fledged project](http://nychenge.com) that was run by [WNYC](http://www.wnyc.org/articles/wnyc-news/2013/jul/12/yes-manhattanhenge-also-park-slopehenge/)
34 |
35 | #### Share your private maps
36 |
37 | For when you have collaborators that you trust, a lot, but don't trust them enough not to mess up tables in your CartoDB account. Using this page, you can give them your account name, api_key (and trust them not to share it or leave it laying around!), table and they can view the maps you made. I'm improving the example to add support for the basemaps you select, infowindows, etc., but it will take a couple of weeks to update.
38 |
39 | - [demo](http://andrewxhill.github.io/cartodb-examples/private-maps/index.html)
40 |
41 | #### Two maps, one center
42 |
43 | This is a really quick mod of the CartoDB double map template. In the [orignal template](http://cartodb.github.io/cartodb-publishing-templates/doublemap/), both maps have the same center, and moving one map moves the other. A question on our forum from [Michael Keller](https://twitter.com/mhkeller) prompted me to make this version, where there is only a single center, so one map bleeds into the other. Hacky hacky
44 |
45 | - [demo](http://andrewxhill.github.io/cartodb-examples/double-map-alt/index.html)
46 |
47 | #### Smart markers
48 |
49 | Often times users will want to create a marker layer where the markers come from CartoDB. If you have a large dataset, this can get annoying, dealing with overlaps and not loading too much data at once. In this example, I create a leaflet marker layer limited to the current zoom and bounding box. Within the view, I limit it to one marker per ever 40px square. With every pan and zoom I query new points to fill the space.
50 |
51 | - [demo](http://andrewxhill.github.io/cartodb-examples/smart-markers/index.html)
52 |
53 | #### Writable Polygon
54 |
55 | This example uses PostgreSQL to turn CartoDB into a form submit endpoint without any proxy layer.
56 |
57 | - [code](http://github.com/andrewxhill/cartodb-examples/blob/master/writable)
58 | - [demo](http://andrewxhill.github.io/cartodb-examples/writable/index.html)
59 |
60 | #### Changing polygon intensity over time [D3]
61 |
62 | This example uses the CartoDB SQL API and D3 to show a changing intensity in state polygons over time.
63 |
64 | - [code](http://github.com/andrewxhill/cartodb-examples/blob/master/intensity-time)
65 | - [demo](http://andrewxhill.github.io/cartodb-examples/intensity-time/index.html)
66 |
67 | #### Experiments with Videos, CartoDB, and Leaflet
68 |
69 | This example makes a video's playing timestamp into a control for a map
70 |
71 | - [demo](http://andrewxhill.github.io/cartodb-examples/videomap/html5/index.html)
72 |
73 | Quick test to create a Leaflet plugin for a VideoOVerlay object
74 |
75 | with canvas,
76 |
77 | - [demo](http://andrewxhill.github.io/cartodb-examples/videomap/inset/index.html)
78 |
79 | or with Vimeo,
80 |
81 | - [demo](http://andrewxhill.github.io/cartodb-examples/videomap/video-inset/index.html)
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/basic-maps/layer-selector/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Gmaps layer toggle | CartoDB.js
5 |
6 |
7 |
8 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 | Manhattan Streets
53 | Manhattan Stores
54 | Manhattan Density
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
131 |
132 |
--------------------------------------------------------------------------------
/double-map-alt/aww-rats.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Rats and Trees in NYC
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
30 |
31 |
32 |
33 |
Rats and Trees in NYC
34 |
I have no idea what the relationship is between these two datasets, but I wanted to see them together :)
35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 |
46 |
47 |
48 |
Description block
49 |
50 |
51 |
52 |
53 |
54 |
55 |
The left panel is all rat sightenings reported to 311 between 2009 and 2013. The right panel are all the trees in NYC.
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
69 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/double-map-alt/css/dark-theme.css:
--------------------------------------------------------------------------------
1 | /* Change the styles below in order to customize your template */
2 |
3 | body{font-family: Helvetica, Arial; font-weight: regular; font-size: 15px; color: #CBCBCB; background-color: #333}
4 | h1{font-weight: bold; font-size: 31px; letter-spacing: -1px; color: #FFF; line-height: 33px; *margin-top:20px;}
5 | h3{font-weight: bold; font-size: 12px; color: #777; text-transform: uppercase; margin: 10px 0 0 0;}
6 | p{margin: 8px 0 20px 0; line-height: 18px;}
7 | a, a:visited{color: #72B6E5; text-decoration: none;}
8 | a:hover{text-decoration: underline;}
9 |
10 | .context{font-family: Helvetica, Arial; font-size: 13px; color: #999; padding: 10px 0 0 0;}
11 | .subheader{border-bottom: 1px solid #555;}
12 | .footer{border-top: 1px solid #555; margin-top: 20px;}
13 | .map{background-color:#eee; border-bottom: 1px solid #000; border-top: 1px solid #000; height: 440px; margin: 10px 0 25px 0;}
14 | .titleBlock{text-align: center;}
15 |
--------------------------------------------------------------------------------
/double-map-alt/css/light-theme.css:
--------------------------------------------------------------------------------
1 | /* Change the styles below in order to customize your template */
2 |
3 | body{font-family: Helvetica, Arial; font-weight: regular; font-size: 15px; color: #555; background-color: #FFF}
4 | h1{font-weight: bold; font-size: 31px; letter-spacing: -1px; color: #333; line-height: 33px; *margin-top:20px;}
5 | h3{font-weight: bold; font-size: 12px; color: #CCC; text-transform: uppercase; margin: 10px 0 0 0;}
6 | p{margin: 8px 0 20px 0; line-height: 18px;}
7 | a, a:visited{color: #397DB8; text-decoration: none;}
8 | a:hover{text-decoration: underline;}
9 |
10 | .context{font-family: Helvetica, Arial; font-size: 13px; color: #666; padding: 10px 0 0 0;}
11 | .subheader{border-bottom: 1px solid #ddd;}
12 | .footer{border-top: 1px solid #ddd; margin-top: 20px;}
13 | .map{background-color:#eee; border-bottom: 1px solid #ccc; border-top: 1px solid #ccc; height: 440px; margin: 10px 0 25px 0;}
14 | .titleBlock{text-align: center;}
15 |
--------------------------------------------------------------------------------
/double-map-alt/css/makeitresponsive.css:
--------------------------------------------------------------------------------
1 | /* Here are the styles that makes the template responsive */
2 |
3 | .row {width: 96%; max-width: 960px; margin: 0 auto; text-align: center;}
4 | .row:before, .row:after {content: " "; display: table;}
5 | .row:after {clear: both;}
6 | .row .row {width: 100%;}
7 |
8 | .col {display: inline; float: left; margin: 0 1%; position: relative;}
9 | .col .col {margin: 0 2%;}
10 | .col .col:first-child {margin-left: 0;}
11 | .col .col:last-child {margin-right: 0;}
12 |
13 | .span1 {width: 4.25%;}
14 | .span2 {width: 10.5%;}
15 | .span3 {width: 16.75%;}
16 | .span4 {width: 23%;}
17 | .span5 {width: 29.5%;}
18 | .span6 {width: 35.5%;}
19 | .span7 {width: 41.75%;}
20 | .span8 {width: 48%;}
21 | .span9 {width: 54.25%;}
22 | .span10 {width: 60.5%;}
23 | .span11 {width: 66.75%;}
24 | .span12 {width: 73%;}
25 | .span13 {width: 79.25%;}
26 | .span14 {width: 85.5%;}
27 | .span15 {width: 91.75%;}
28 | .span16 {width: 98%;}
29 |
30 | @media only screen and (max-width: 480px) {
31 | .col.span8 {margin: 0; width: 100%;}
32 | .col.span12 {margin: 0; width: 100%;}
33 | .col.span4 {margin: 0; text-align: left;}
34 | .col.footer {margin: 30px 0 0 0;}
35 | .col.span4.titleBlock h3 {margin: 5px 0 0 0;}
36 | .col.span2.empty {margin: 0; width: 100%; display: none}
37 | }
38 |
--------------------------------------------------------------------------------
/intensity-time/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/live-map/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Easy example | CartoDB.js
5 |
6 |
7 |
8 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/manhattanhenge/city/chicago.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sunset map
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
106 |
107 |
108 |
109 |
110 |
111 | start animation
112 |
113 |
114 |
115 |
116 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/manhattanhenge/city/dc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sunset map
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
106 |
107 |
108 |
109 |
110 |
111 | start animation
112 |
113 |
114 |
115 |
116 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/manhattanhenge/city/nyc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sunset map
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
106 |
107 |
108 |
109 |
110 |
111 | start animation
112 |
113 |
114 |
115 |
116 |
117 |
120 |
121 |
--------------------------------------------------------------------------------
/manhattanhenge/city/sf.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sunset map
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
106 |
107 |
108 |
109 |
110 |
111 | start animation
112 |
113 |
114 |
115 |
116 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/manhattanhenge/img/bk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/bk.png
--------------------------------------------------------------------------------
/manhattanhenge/img/chicago.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/chicago.png
--------------------------------------------------------------------------------
/manhattanhenge/img/dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/dc.png
--------------------------------------------------------------------------------
/manhattanhenge/img/nyc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/nyc.png
--------------------------------------------------------------------------------
/manhattanhenge/img/nyc_districts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/nyc_districts.png
--------------------------------------------------------------------------------
/manhattanhenge/img/pt3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/pt3.png
--------------------------------------------------------------------------------
/manhattanhenge/img/sf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/sf.png
--------------------------------------------------------------------------------
/manhattanhenge/img/veknik_osm_london.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrewxhill/cartodb-examples/800ff4c519611e3bdac58bb0b3f471ea85506c61/manhattanhenge/img/veknik_osm_london.png
--------------------------------------------------------------------------------
/manhattanhenge/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Gallery
7 |
8 |
9 |
10 |
11 |
68 |
69 |
70 |
78 |
79 |
80 |
81 |
86 |
87 |
88 |
93 |
94 |
95 |
100 |
101 |
102 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/manhattanhenge/js/carto.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // Carto stylesheets support
3 | //
4 | // this is basically a hack on top of branch browser of carto
5 | // repository: Compiles carto to javascript shader
6 | //========================================
7 |
8 | (function(VECNIK) {
9 |
10 | // monkey patch less classes
11 | tree.Value.prototype.toJS = function() {
12 | var v = this.value[0].value[0];
13 | val = v.toString();
14 | if(v.is === "color") {
15 | val = "'" + val + "'";
16 | }
17 | return "_value = " + val + ";"
18 | }
19 |
20 | tree.Selector.prototype.toJS = function() {
21 | var self = this;
22 | var opMap = {
23 | '=': '==='
24 | };
25 | var zoom = "(" + self.zoom + " & (1 << ctx.zoom))";
26 | return [zoom].concat(
27 | _.map(this.filters, function(filter) {
28 | var op = filter.op;
29 | if(op in opMap) {
30 | op = opMap[op];
31 | }
32 | var val = filter.val;
33 | if(filter._val !== undefined) {
34 | val = filter._val.toString(true);
35 | }
36 |
37 | var attrs = "data";
38 | return attrs + "." + filter.key + " " + op + " " + val;
39 | })
40 | ).join(" && ");
41 | }
42 |
43 | tree.Ruleset.prototype.toJS = function() {
44 | var shaderAttrs = {};
45 | var _if = this.selectors[0].toJS();
46 | _.each(this.rules, function(rule) {
47 | if(rule instanceof tree.Rule) {
48 | shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
49 | if (_if) {
50 | shaderAttrs[rule.name].push(
51 | "if(" + _if + "){" + rule.value.toJS() + "}"
52 | );
53 | } else {
54 | shaderAttrs[rule.name].push(rule.value.toJS());
55 | }
56 | } else {
57 | if (rule instanceof tree.Ruleset) {
58 | var sh = rule.toJS();
59 | for(var v in sh) {
60 | shaderAttrs[v] = shaderAttrs[v] || [];
61 | for(var attr in sh[v]) {
62 | shaderAttrs[v].push(sh[v][attr]);
63 | }
64 | }
65 | }
66 | }
67 | });
68 | return shaderAttrs;
69 | }
70 |
71 | function createFn(ops) {
72 | var body = ops.join('\n');
73 | return Function("data","ctx", "var _value = null; " + body + "; return _value; ");
74 | }
75 |
76 | function toCartoShader(ruleset) {
77 | var shaderAttrs = {};
78 | shaderAttrs = ruleset.rules[0].toJS();
79 | try {
80 | for(var attr in shaderAttrs) {
81 | shaderAttrs[attr] = createFn(shaderAttrs[attr]);
82 | }
83 | }
84 | catch(e) {
85 | console.log("error creating shader");
86 | console.log(e);
87 | return null;
88 | }
89 |
90 |
91 | return shaderAttrs;
92 | }
93 |
94 | /**
95 | * compile from Carto style to javascript shader
96 | */
97 | var compile = function(style, callback) {
98 |
99 | var parse_env = {
100 | error: function(obj) {
101 | console.log("ERROR");
102 | }
103 | };
104 |
105 | var parser = new carto.Parser(parse_env);
106 |
107 | parser.parse(style, function(err, ruleset) {
108 | if(!err) {
109 | var shader = toCartoShader(ruleset);
110 | callback(shader);
111 | } else {
112 | callback(null);
113 | }
114 | });
115 | }
116 |
117 | var init = function(callback) {
118 | carto_initialize(carto, './reference.json', function(carto) {
119 | VECNIK.Carto._carto = carto;
120 | if(callback) callback(carto);
121 | });
122 | }
123 |
124 | VECNIK.Carto = {
125 | init: init,
126 | compile: compile
127 | };
128 |
129 | })(VECNIK);
130 |
131 | if (typeof module !== 'undefined' && module.exports) {
132 | }
133 |
--------------------------------------------------------------------------------
/manhattanhenge/js/cartodb.provider.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // CartoDB data provider
4 | //========================================
5 |
6 | (function(VECNIK) {
7 |
8 | function CartoDBSQLAPI(opts) {
9 | this.projection = new VECNIK.MercatorProjection();
10 | this.opts = opts;
11 | this.base_url = 'http://' + opts.user + ".cartodb.com/api/v2/sql";
12 |
13 | //set defaults
14 | this.opts.ENABLE_SIMPLIFY = VECNIK.settings.get('ENABLE_SIMPLIFY');
15 | this.opts.ENABLE_SNAPPING = VECNIK.settings.get('ENABLE_SNAPPING');
16 | this.opts.ENABLE_CLIPPING = VECNIK.settings.get('ENABLE_CLIPPING');
17 | this.opts.ENABLE_FIXING = VECNIK.settings.get('ENABLE_FIXING');
18 | }
19 |
20 | CartoDBSQLAPI.prototype.debug = function(w) {
21 | if(this.opts.debug) {
22 | //console.log(w);
23 | }
24 | }
25 |
26 | CartoDBSQLAPI.prototype._sql_url = function(sql) {
27 | var self = this;
28 | this.debug(sql);
29 | return this.base_url + "?q=" + encodeURIComponent(sql) + "&format=geojson&dp=6";
30 | }
31 |
32 | CartoDBSQLAPI.prototype.get_tile_data_sql = function(projection, table, x, y, zoom) {
33 | return VECNIK.CartoDB.SQL(projection, table, x, y, zoom, this.opts);
34 | };
35 |
36 | CartoDBSQLAPI.prototype.url = function(coordinates) {
37 | var projection = this.projection;
38 | var opts = this.opts;
39 | var table = opts.table;
40 | var prj = this.projection;
41 | var sql = this.get_tile_data_sql(prj, table, coordinates.column, coordinates.row, coordinates.zoom);
42 | var sql_url = this._sql_url(sql);
43 | return sql_url;
44 | }
45 |
46 | VECNIK.CartoDB = VECNIK.CartoDB || {};
47 | VECNIK.CartoDB.API = CartoDBSQLAPI;
48 |
49 | })(VECNIK);
50 |
--------------------------------------------------------------------------------
/manhattanhenge/js/cartodb.sql.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // sql generator for cartodb
3 | //========================================
4 |
5 | var VECNIK = VECNIK || {};
6 |
7 | (function(VECNIK) {
8 |
9 | var sql = function(projection, table, x, y, zoom, opts) {
10 |
11 | opts = opts || {
12 | ENABLE_CLIPPING: false,
13 | ENABLE_SIMPLIFY: false,
14 | ENABLE_FIXING: false,
15 | ENABLE_SNAPPING: false
16 | };
17 | var bbox = projection.tileBBox(x, y, zoom);
18 | var geom_column = '"the_geom"';
19 | var geom_column_orig = '"the_geom"';
20 | var id_column = 'cartodb_id';
21 | var TILE_SIZE = 256;
22 | var tile_pixel_width = TILE_SIZE;
23 | var tile_pixel_height = TILE_SIZE;
24 |
25 | //console.log('-- ZOOM: ' + zoom);
26 |
27 | var tile_geo_width = bbox[1].lng() - bbox[0].lng();
28 | var tile_geo_height = bbox[1].lat() - bbox[0].lat();
29 |
30 | var pixel_geo_width = tile_geo_width / tile_pixel_width;
31 | var pixel_geo_height = tile_geo_height / tile_pixel_height;
32 |
33 | //console.log('-- PIXEL_GEO_SIZE: '
34 | // + pixel_geo_width + ' x ' + pixel_geo_height);
35 |
36 | var pixel_geo_maxsize = Math.max(pixel_geo_width, pixel_geo_height);
37 | //console.log('-- MAX_SIZE: ' + pixel_geo_maxsize);
38 |
39 | var tolerance = pixel_geo_maxsize / 2;
40 | //console.log('-- TOLERANCE: ' + tolerance);
41 |
42 | // simplify
43 | var ENABLE_SIMPLIFY = opts.ENABLE_SIMPLIFY;
44 | if ( ENABLE_SIMPLIFY ) {
45 | geom_column = 'ST_Simplify(' + geom_column + ', ' + tolerance + ')';
46 | // may change type
47 | geom_column = 'ST_CollectionExtract(' + geom_column + ', ST_Dimension( '
48 | + geom_column_orig + ') + 1 )';
49 | }
50 |
51 | // snap to a pixel grid
52 | var ENABLE_SNAPPING = opts.ENABLE_SNAPPING;
53 | if ( ENABLE_SNAPPING ) {
54 | geom_column = 'ST_SnapToGrid(' + geom_column + ', '
55 | + pixel_geo_maxsize + ')';
56 | // may change type
57 | geom_column = 'ST_CollectionExtract(' + geom_column + ', ST_Dimension( '
58 | + geom_column_orig + ') + 1 )';
59 | }
60 |
61 | // This is the query bounding box
62 | var sql_env = "ST_MakeEnvelope("
63 | + bbox[0].lng() + "," + bbox[0].lat() + ","
64 | + bbox[1].lng() + "," + bbox[1].lat() + ", 4326)";
65 |
66 | // clip
67 | var ENABLE_CLIPPING = opts.ENABLE_CLIPPING;
68 | if ( ENABLE_CLIPPING ) {
69 |
70 | // This is a slightly enlarged version of the query bounding box
71 | var sql_env_exp = 'ST_Expand(' + sql_env + ', '
72 | + ( pixel_geo_maxsize * 2 ) + ')';
73 | // Also must be snapped to the grid ...
74 | sql_env_exp = 'ST_SnapToGrid(' + sql_env_exp + ','
75 | + pixel_geo_maxsize + ')';
76 |
77 | // snap to box
78 | geom_column = 'ST_Snap(' + geom_column + ', ' + sql_env_exp
79 | + ', ' + pixel_geo_maxsize + ')';
80 |
81 | // Make valid (both ST_Snap and ST_SnapToGrid and ST_Expand
82 | var ENABLE_FIXING = opts.ENABLE_FIXING;
83 | if ( ENABLE_FIXING ) {
84 | // NOTE: up to PostGIS-2.0.0 beta5 ST_MakeValid did not accept
85 | // points nor GeometryCollection objects
86 | geom_column = 'CASE WHEN ST_Dimension('
87 | + geom_column + ') = 0 OR GeometryType('
88 | + geom_column + ") = 'GEOMETRYCOLLECTION' THEN "
89 | + geom_column + ' ELSE ST_CollectionExtract(ST_MakeValid('
90 | + geom_column + '), ST_Dimension(' + geom_column_orig
91 | + ') + 1 ) END';
92 | }
93 |
94 | // clip by box
95 | geom_column = 'ST_Intersection(' + geom_column
96 | + ', ' + sql_env_exp + ')';
97 | }
98 |
99 | var columns = id_column + ',' + geom_column + ' as the_geom';
100 | if(opts.columns) {
101 | columns += ',';
102 | columns += opts.columns.join(',')
103 | columns += ' ';
104 | }
105 |
106 | // profiling only
107 | var COUNT_ONLY = opts.COUNT_ONLY || false;
108 | if ( COUNT_ONLY ) {
109 | columns = x + ' as x, ' + y + ' as y, sum(st_npoints('
110 | + geom_column + ')) as the_geom';
111 | }
112 |
113 | var sql = "select " + columns +" from " + table;
114 | sql += " WHERE the_geom && " + sql_env;
115 | //sql += " LIMIT 100";
116 |
117 | //console.log('-- SQL: ' + sql);
118 |
119 | return sql;
120 | };
121 |
122 | VECNIK.CartoDB = VECNIK.CartoDB || {};
123 | VECNIK.CartoDB.SQL = sql;
124 |
125 | })(VECNIK);
126 |
127 | if (typeof module !== 'undefined' && module.exports) {
128 | module.exports.CartoDBSQL = VECNIK.CartoDB.SQL;
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/manhattanhenge/js/core.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // Core
3 | //
4 | // base classes
5 | //========================================
6 |
7 | // create root scope if not exists
8 | var VECNIK = VECNIK || {};
9 |
10 | (function(VECNIK) {
11 |
12 | //========================================
13 | // Events
14 | //
15 | // event management
16 | //========================================
17 |
18 | function Event() {}
19 | Event.prototype.on = function(evt, callback) {
20 | var cb = this.callbacks = this.callbacks || {};
21 | var l = cb[evt] || (cb[evt] = []);
22 | l.push(callback);
23 | };
24 |
25 | Event.prototype.emit = function(evt) {
26 | var c = this.callbacks && this.callbacks[evt];
27 | for(var i = 0; c && i < c.length; ++i) {
28 | c[i].apply(this, Array.prototype.slice.call(arguments, 1));
29 | }
30 | };
31 |
32 |
33 | // http get
34 | // should be improved
35 | function get(url, callback) {
36 | var mygetrequest= new XMLHttpRequest();
37 | mygetrequest.onreadystatechange=function() {
38 | if (mygetrequest.readyState == 4){
39 | if (mygetrequest.status == 200){
40 | callback(JSON.parse(mygetrequest.responseText));
41 | }
42 | else {
43 | //error
44 | }
45 | }
46 | };
47 | mygetrequest.open("GET", url, true)
48 | mygetrequest.send(null)
49 | }
50 |
51 | //========================================
52 | // model
53 | //
54 | // pretty basic model funcionallity
55 | //========================================
56 |
57 | function Model() {
58 | //this.data = {}; // serializable data
59 | }
60 |
61 | Model.prototype = new Event();
62 |
63 | Model.prototype.set = function(data, silent) {
64 | this.data = this.data || {};
65 | for(var v in data) {
66 | if(data.hasOwnProperty(v)) {
67 | this.data[v] = data[v];
68 | }
69 | }
70 | if(!silent) {
71 | this.emit('change', this.data);
72 | }
73 | };
74 |
75 | Model.prototype.get = function(attr, def) {
76 | if(this.data) {
77 | if(attr in this.data) {
78 | return this.data[attr];
79 | }
80 | return def;
81 | }
82 | return def;
83 | };
84 |
85 | /**
86 | * delete the attribute
87 | */
88 | Model.prototype.unset = function(attr, silent) {
89 | delete this.data[attr];
90 | if(!silent) {
91 | this.emit('change', this.data);
92 | }
93 | };
94 |
95 | Model.prototype.destroy = function() {
96 | this.emit('destroy');
97 | delete this.data;
98 | };
99 |
100 |
101 | VECNIK.Event = Event;
102 | VECNIK.Model = Model;
103 | VECNIK.get = get;
104 |
105 | })(VECNIK);
106 |
107 | if (typeof module !== 'undefined' && module.exports) {
108 | module.exports.Event = VECNIK.Event;
109 | module.exports.Model = VECNIK.Model;
110 | }
111 |
--------------------------------------------------------------------------------
/manhattanhenge/js/geojson.provider.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // CartoDB data provider
4 | //========================================
5 |
6 | (function(VECNIK) {
7 |
8 | function GeoJSONTile(opts) {
9 | this.opts = opts;
10 | this.template = opts.template;
11 | }
12 |
13 | GeoJSONTile.prototype.url = function(coord) {
14 | return this.template
15 | .replace('{z}', coord.zoom.toFixed(0))
16 | .replace('{x}', coord.column.toFixed(0))
17 | .replace('{y}', coord.row.toFixed(0));
18 | }
19 |
20 | VECNIK.GeoJSONTile = GeoJSONTile;
21 |
22 | })(VECNIK);
23 |
--------------------------------------------------------------------------------
/manhattanhenge/js/geometry.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // geometry conversion
4 | //========================================
5 |
6 | var VECNIK = VECNIK || {};
7 |
8 | (function(VECNIK) {
9 |
10 | var LatLng = VECNIK.LatLng;
11 | var Point = VECNIK.Point;
12 |
13 | //stats
14 | var stats = {
15 | vertices: 0
16 | };
17 |
18 | var latlng = new LatLng(0, 0);
19 | var prj = new VECNIK.MercatorProjection();
20 |
21 | function map_latlon(ll, x, y, zoom) {
22 | latlng.latitude = ll[1];
23 | latlng.longitude = ll[0];
24 | stats.vertices++;
25 | var point = prj.latLngToTilePoint(latlng, x, y, zoom);
26 | //point.x = point.x >> 0;
27 | //point.y = point.y >> 0;
28 | return point;
29 | }
30 |
31 | var primitive_conversion = {
32 | 'LineString': function(x, y, zoom, coordinates) {
33 | var converted = [];
34 | var pc = primitive_conversion['Point'];
35 | for(var i=0; i < coordinates.length; ++i) {
36 | converted.push(pc(x, y, zoom, coordinates[i]));
37 | }
38 | return converted;
39 | },
40 |
41 | 'Point': function(x, y, zoom, coordinates) {
42 | return map_latlon(coordinates, x, y, zoom);
43 | },
44 |
45 | 'MultiPoint': function(x, y, zoom, coordinates) {
46 | var converted = [];
47 | var pc = primitive_conversion['Point'];
48 | for(var i=0; i < coordinates.length; ++i) {
49 | converted.push(pc(x, y, zoom, coordinates[i]));
50 | }
51 | return converted;
52 | },
53 | //do not manage inner polygons!
54 | 'Polygon': function(x, y, zoom, coordinates) {
55 | if(coordinates[0]) {
56 | var coords = [];
57 | for(var i=0; i < coordinates[0].length; ++i) {
58 | coords.push(map_latlon(coordinates[0][i], x, y, zoom));
59 | }
60 | return [coords];
61 | }
62 | return null;
63 | },
64 | 'MultiPolygon': function(x, y, zoom, coordinates) {
65 | var polys = [];
66 | var poly;
67 | var pc = primitive_conversion['Polygon'];
68 | for(var i=0; i < coordinates.length; ++i) {
69 | poly = pc(x, y, zoom, coordinates[i]);
70 | if(poly)
71 | polys.push(poly);
72 | }
73 | return polys;
74 | }
75 | };
76 |
77 | var project_geometry = function(geometry, zoom, x, y) {
78 | var conversor = primitive_conversion[geometry.type];
79 | if(conversor) {
80 | return conversor(x, y , zoom, geometry.coordinates);
81 | }
82 | };
83 |
84 | VECNIK.project_geometry = project_geometry;
85 | VECNIK.geometry_stats = stats;
86 |
87 | })(VECNIK);
88 |
89 | if (typeof module !== 'undefined' && module.exports) {
90 | module.exports.project_geometry = VECNIK.project_geometry;
91 | }
92 | if (typeof self !== 'undefined') {
93 | self.VECNIK = VECNIK;
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/manhattanhenge/js/mercator.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // Mercator projection
4 | //========================================
5 | //
6 | var VECNIK = VECNIK || {};
7 |
8 | (function(VECNIK) {
9 |
10 | var TILE_SIZE = 256;
11 |
12 | // todo: move outside
13 | function Point(x, y) {
14 | this.x = x || 0;
15 | this.y = y || 0;
16 | }
17 |
18 | function LatLng(lat, lon) {
19 | this.latitude = lat || 0;
20 | this.longitude = lon || 0;
21 | }
22 |
23 | LatLng.prototype.lat = function() {
24 | return this.latitude;
25 | }
26 |
27 | LatLng.prototype.lng = function() {
28 | return this.longitude;
29 | }
30 |
31 | function bound(value, opt_min, opt_max) {
32 | if (opt_min != null) value = Math.max(value, opt_min);
33 | if (opt_max != null) value = Math.min(value, opt_max);
34 | return value;
35 | }
36 |
37 | function degreesToRadians(deg) {
38 | return deg * (Math.PI / 180);
39 | }
40 |
41 | function radiansToDegrees(rad) {
42 | return rad / (Math.PI / 180);
43 | }
44 |
45 | function MercatorProjection() {
46 | this.pixelOrigin_ = new Point(TILE_SIZE / 2, TILE_SIZE / 2);
47 | this.pixelsPerLonDegree_ = TILE_SIZE / 360;
48 | this.pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);
49 | }
50 |
51 | MercatorProjection.prototype.fromLatLngToPoint = function (latLng, opt_point) {
52 | var me = this;
53 | var point = opt_point || new Point(0, 0);
54 | var origin = me.pixelOrigin_;
55 |
56 | point.x = origin.x + latLng.lng() * me.pixelsPerLonDegree_;
57 |
58 | // NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
59 | // 89.189. This is about a third of a tile past the edge of the world
60 | // tile.
61 | var siny = bound(Math.sin(degreesToRadians(latLng.lat())), -0.9999,
62 | 0.9999);
63 | point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) *
64 | -me.pixelsPerLonRadian_;
65 | return point;
66 | };
67 |
68 | MercatorProjection.prototype.fromPointToLatLng = function (point) {
69 | var me = this;
70 | var origin = me.pixelOrigin_;
71 | var lng = (point.x - origin.x) / me.pixelsPerLonDegree_;
72 | var latRadians = (point.y - origin.y) / -me.pixelsPerLonRadian_;
73 | var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) -
74 | Math.PI / 2);
75 | return new LatLng(lat, lng);
76 | };
77 |
78 | MercatorProjection.prototype.tileBBox = function(x, y, zoom) {
79 | var numTiles = 1 << zoom;
80 | var inc = TILE_SIZE/numTiles;
81 | var px = x*TILE_SIZE/numTiles;
82 | var py = y*TILE_SIZE/numTiles;
83 | return [
84 | this.fromPointToLatLng(new Point(px, py + inc)),
85 | this.fromPointToLatLng(new Point(px + inc, py))
86 | ];
87 | };
88 |
89 | MercatorProjection.prototype.tilePoint = function(x, y, zoom) {
90 | var numTiles = 1 << zoom;
91 | var px = x*TILE_SIZE;
92 | var py = y*TILE_SIZE;
93 | return [px, py];
94 | };
95 |
96 | MercatorProjection.prototype.latLngToTilePoint = function(latLng, x, y, zoom) {
97 | var numTiles = 1 << zoom;
98 | var projection = this;
99 | var worldCoordinate = projection.fromLatLngToPoint(latLng);
100 | var pixelCoordinate = new Point(
101 | worldCoordinate.x * numTiles,
102 | worldCoordinate.y * numTiles);
103 | var tp = this.tilePoint(x, y, zoom);
104 | return new Point(
105 | Math.floor(pixelCoordinate.x - tp[0]),
106 | Math.floor(pixelCoordinate.y - tp[1]));
107 | };
108 |
109 | MercatorProjection.prototype.latLngToTile = function(latLng, zoom) {
110 | var numTiles = 1 << zoom;
111 | var projection = this;
112 | var worldCoordinate = projection.fromLatLngToPoint(latLng);
113 | var pixelCoordinate = new Point(
114 | worldCoordinate.x * numTiles,
115 | worldCoordinate.y * numTiles);
116 | return new Point(
117 | Math.floor(pixelCoordinate.x / TILE_SIZE),
118 | Math.floor(pixelCoordinate.y / TILE_SIZE));
119 | };
120 |
121 | VECNIK.LatLng = LatLng;
122 | VECNIK.Point = Point;
123 | VECNIK.MercatorProjection = MercatorProjection;
124 |
125 | })(VECNIK);
126 |
127 | if (typeof module !== 'undefined' && module.exports) {
128 | module.exports.MercatorProjection = VECNIK.MercatorProjection;
129 | module.exports.LatLng = VECNIK.LatLng;
130 | module.exports.Point = VECNIK.Point;
131 | }
132 |
133 | if (typeof self !== 'undefined') {
134 | self.VECNIK = VECNIK;
135 | }
136 |
--------------------------------------------------------------------------------
/manhattanhenge/js/model.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // vecnik models
3 | //========================================
4 |
5 | (function(VECNIK) {
6 |
7 | // utility
8 | function Profiler(name){
9 | this.t0 = 0;
10 | this.unit = '';
11 | }
12 | Profiler.prototype.start = function(unit) {
13 | this.t0 = new Date().getTime();
14 | this.unit = unit || '';
15 | }
16 | Profiler.prototype.end= function() {
17 | var t = new Date().getTime() - this.t0;
18 | //console.log("PROFILE - " + this.unit + ":" + t);
19 | return t;
20 | }
21 |
22 | //========================================
23 | // tile model
24 | //========================================
25 | function Tile(x, y, zoom) {
26 | this.x = x;
27 | this.y = y;
28 | this.zoom = zoom;
29 | this.on('change', this.precache.bind(this))
30 | this.stats = {
31 | conversion_time: 0,
32 | vertices: 0,
33 | primitive_count: 0
34 | };
35 | this.profiler = new Profiler('tile');
36 | }
37 |
38 | Tile.prototype = new VECNIK.Model();
39 |
40 | Tile.prototype.key = function() {
41 | return [this.x, this.y, this.zoom].join('-');
42 | }
43 |
44 | Tile.prototype.geometry = function() {
45 | return this.get('geometry');
46 | }
47 |
48 | Tile.prototype.precache = function() {
49 | var self = this;
50 | var geometry = [];
51 | this.profiler.start('conversion_time');
52 | var primitives = this.data.features;
53 | var vertex_count = VECNIK.geometry_stats.vertices;
54 | if(VECNIK.settings.get('WEBWORKERS') && typeof Worker !== undefined) {
55 | var worker = new Worker('../js/projector.worker.js');
56 | worker.onmessage = function(ev) {
57 | self.set({geometry: ev.data.geometry}, true);
58 | self.unset('features', true);
59 | self.emit('geometry_ready');
60 | };
61 | worker.postMessage({
62 | primitives: primitives,
63 | zoom: this.zoom,
64 | x: this.x,
65 | y: this.y
66 | });
67 |
68 | } else {
69 | for(var i = 0; i < primitives.length; ++i) {
70 | var p = primitives[i];
71 | if(p.geometry) {
72 | var converted = VECNIK.project_geometry(p.geometry, this.zoom, this.x, this.y);
73 | if(converted && converted.length !== 0) {
74 | geometry.push({
75 | vertexBuffer: converted,
76 | type: p.geometry.type,
77 | metadata: p.properties
78 | });
79 | } else {
80 | delete p.geometry.coordinates;
81 | }
82 | }
83 | }
84 | this.set({geometry: geometry}, true);
85 | this.unset('features', true);
86 | this.emit('geometry_ready');
87 | }
88 | this.stats.vertices = VECNIK.geometry_stats.vertices - vertex_count;
89 | this.stats.primitive_count = primitives.length;
90 | this.stats.conversion_time = this.profiler.end();
91 | }
92 |
93 |
94 | //========================================
95 | // tile manager
96 | //========================================
97 |
98 | function TileManager(dataProvider) {
99 | this.tiles = {};
100 | this.dataProvider = dataProvider;
101 | }
102 |
103 | TileManager.prototype.tileIndex= function(coordinates) {
104 | return coordinates.toKey();
105 | }
106 |
107 | TileManager.prototype.get = function(coordinates) {
108 | return this.tiles[this.tileIndex(coordinates)];
109 | }
110 |
111 | TileManager.prototype.destroy= function(coordinates) {
112 | var tile = this.tiles[this.tileIndex(coordinates)];
113 | if(tile) {
114 | tile.destroy();
115 | //console.log("removing " + this.tileIndex(coordinates));
116 | delete this.tiles[this.tileIndex(coordinates)];
117 | }
118 | }
119 |
120 | TileManager.prototype.add = function(coordinates) {
121 | //console.log("adding" + this.tileIndex(coordinates));
122 | var tile = this.tiles[this.tileIndex(coordinates)] = new Tile(
123 | coordinates.column,
124 | coordinates.row,
125 | coordinates.zoom
126 | );
127 |
128 | VECNIK.get(this.dataProvider.url(coordinates), function(data) {
129 | tile.set(data);
130 | });
131 | return tile;
132 | }
133 |
134 | VECNIK.Tile = Tile;
135 | VECNIK.TileManager = TileManager;
136 | VECNIK.Profiler = Profiler;
137 |
138 | })(VECNIK);
139 |
140 | if (typeof module !== 'undefined' && module.exports) {
141 | module.exports.Tile = VECNIK.Tile;
142 | module.exports.TileManager = VECNIK.TileManager;
143 | }
144 |
145 |
146 |
--------------------------------------------------------------------------------
/manhattanhenge/js/projector.worker.js:
--------------------------------------------------------------------------------
1 | importScripts('../js/mercator.js');
2 | importScripts('../js/geometry.js');
3 |
4 | self.onmessage = function(event) {
5 | var data = event.data;
6 | var primitives = data.primitives;
7 | var geometry = [];
8 | for(var i = 0; i < primitives.length; ++i) {
9 | var p = primitives[i];
10 | if(p.geometry) {
11 | var converted = VECNIK.project_geometry(p.geometry,
12 | data.zoom, data.x, data.y);
13 | if(converted && converted.length !== 0) {
14 | geometry.push({
15 | vertexBuffer: converted,
16 | type: p.geometry.type,
17 | metadata: p.properties
18 | });
19 | }
20 | }
21 | }
22 | self.postMessage({geometry: geometry});
23 | };
24 |
--------------------------------------------------------------------------------
/manhattanhenge/js/renderer.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // vecnik views
3 | //========================================
4 |
5 | (function(VECNIK) {
6 |
7 | function Renderer() {
8 | var self = this;
9 | var primitive_render = this.primitive_render = {
10 | 'Point': function(ctx, coordinates) {
11 | ctx.save();
12 | var radius = 2;
13 | var p = coordinates;
14 | ctx.translate(p.x, p.y);
15 | ctx.beginPath();
16 | ctx.arc(radius, radius, radius, 0, Math.PI * 2, true);
17 | ctx.closePath();
18 | ctx.fill();
19 | ctx.stroke();
20 | ctx.restore();
21 | },
22 | 'MultiPoint': function(ctx, coordinates) {
23 | var prender = primitive_render['Point'];
24 | for(var i=0; i < coordinates.length; ++i) {
25 | prender(ctx, coordinates[i]);
26 | }
27 | },
28 | 'Polygon': function(ctx, coordinates) {
29 | ctx.beginPath();
30 | var p = coordinates[0][0];
31 | ctx.moveTo(p.x, p.y);
32 | for(var i=0; i < coordinates[0].length; ++i) {
33 | p = coordinates[0][i];
34 | ctx.lineTo(p.x, p.y);
35 | }
36 | ctx.closePath();
37 | ctx.fill();
38 | //ctx.stroke();
39 | },
40 | 'MultiPolygon': function(ctx, coordinates) {
41 | var prender = primitive_render['Polygon'];
42 | for(var i=0; i < coordinates.length; ++i) {
43 | prender(ctx, coordinates[i]);
44 | }
45 | },
46 | 'LineString': function(ctx, coordinates) {
47 | ctx.beginPath();
48 | var p = coordinates[0];
49 | ctx.moveTo(p.x, p.y);
50 | for(var i=0; i < coordinates.length; ++i) {
51 | p = coordinates[i];
52 | ctx.lineTo(p.x, p.y);
53 | }
54 | ctx.stroke();
55 | }
56 | };
57 | }
58 |
59 | Renderer.prototype.render = function(ctx, geometry, zoom, shader) {
60 | var primitive_render = this.primitive_render;
61 | ctx.canvas.width = ctx.canvas.width;
62 | var primitive_type;
63 | if(geometry && geometry.length) {
64 | for(var i = 0; i < geometry.length; ++i) {
65 | var geo = geometry[i];
66 | var primitive_type = geo.type;
67 | var renderer = primitive_render[primitive_type];
68 | if(renderer) {
69 | // render visible tile
70 | var render_context = {
71 | zoom: zoom,
72 | id: i
73 | };
74 | var is_active = true;
75 | if(shader) {
76 | is_active = shader.needs_render(geo.metadata, render_context, primitive_type);
77 | if(is_active) {
78 | shader.reset(ctx, primitive_type);
79 | shader.apply(ctx, geo.metadata, render_context);
80 | }
81 | }
82 | if (is_active) {
83 | renderer(ctx, geo.vertexBuffer);
84 | }
85 | }
86 | }
87 | }
88 | };
89 |
90 | //========================================
91 | // Canvas tile view
92 | //========================================
93 | function CanvasTileView(tile, shader, renderer) {
94 | this.tileSize = new VECNIK.Point(256, 256);
95 | var canvas = document.createElement('canvas');
96 | canvas.width = this.tileSize.x;
97 | canvas.height = this.tileSize.y;
98 | this.ctx = canvas.getContext('2d');
99 | this.canvas = canvas;
100 |
101 | var backCanvas = document.createElement('canvas');
102 | backCanvas.width = this.tileSize.x;
103 | backCanvas.height = this.tileSize.y;
104 | this.backCtx = backCanvas.getContext('2d');
105 | this.backCanvas = backCanvas;
106 |
107 | this.el = canvas;
108 | this.id = tile.key();
109 | this.el.setAttribute('id', tile.key());
110 | var self = this;
111 | this.tile = tile;
112 | var render = function(){self.render();};
113 | tile.on('geometry_ready', render);
114 |
115 | // shader
116 | this.shader = shader;
117 | if(shader) {
118 | shader.on('change', render);
119 | }
120 | this.renderer = renderer || new Renderer();
121 |
122 | this.profiler = new VECNIK.Profiler('tile_render');
123 | this.stats = {
124 | rendering_time: 0
125 | }
126 | }
127 |
128 | CanvasTileView.prototype.remove = function() {
129 | }
130 |
131 | CanvasTileView.prototype.render = function() {
132 | var ctx = this.ctx;
133 |
134 | this.profiler.start('render');
135 | var BACKBUFFER = true;
136 | if(BACKBUFFER) {
137 | this.backCanvas.width = this.backCanvas.width;
138 | this.renderer.render(this.backCtx, this.tile.geometry(), this.tile.zoom, this.shader);
139 | this.canvas.width = this.canvas.width;
140 | this.ctx.drawImage(this.backCanvas, 0, 0);
141 | } else {
142 | this.renderer.render(ctx, this.tile.geometry(), this.tile.zoom, this.shader);
143 | }
144 |
145 | this.stats.rendering_time = this.profiler.end();
146 | }
147 |
148 |
149 | //========================================
150 | // Map view
151 | // manages the list of tiles
152 | //========================================
153 | function CanvasMapView() {
154 | this.tile_views = {};
155 | }
156 |
157 | CanvasMapView.prototype.add = function(canvasview) {
158 | this.tile_views[canvasview.id] = canvasview;
159 | }
160 |
161 | CanvasMapView.prototype.getByElement = function(el) {
162 | return this.tile_views[el.getAttribute('id')];
163 | }
164 |
165 |
166 | VECNIK.Renderer = Renderer;
167 | VECNIK.CanvasTileView = CanvasTileView;
168 | VECNIK.CanvasMapView = CanvasMapView;
169 |
170 | })(VECNIK);
171 |
172 | if (typeof module !== 'undefined' && module.exports) {
173 | }
174 |
175 |
176 |
--------------------------------------------------------------------------------
/manhattanhenge/js/settings.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // Global settings
4 | //========================================
5 |
6 | var VECNIK = VECNIK || {};
7 |
8 | (function(VECNIK) {
9 |
10 | function Settings(defaults) {
11 | this.set(defaults);
12 | }
13 |
14 | Settings.prototype = new VECNIK.Model();
15 |
16 | // default settings
17 | VECNIK.settings = new Settings({
18 | WEBWORKERS: false,
19 | BACKBUFFER: true,
20 | ENABLE_SIMPLIFY: true,
21 | ENABLE_SNAPPING: true,
22 | ENABLE_CLIPPING: true,
23 | ENABLE_FIXING: true
24 | });
25 |
26 | })(VECNIK);
27 |
28 | if (typeof module !== 'undefined' && module.exports) {
29 | module.exports.settings = VECNIK.settings;
30 | }
31 | if (typeof self !== 'undefined') {
32 | self.VECNIK = VECNIK;
33 | }
34 |
--------------------------------------------------------------------------------
/manhattanhenge/js/shader.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // shader
4 | //========================================
5 |
6 | (function(VECNIK) {
7 |
8 | var mapper = {
9 | 'point-color': 'fillStyle',
10 | 'line-color': 'strokeStyle',
11 | 'line-width': 'lineWidth',
12 | 'line-opacity': 'globalAlpha',
13 | 'polygon-fill': 'fillStyle',
14 | 'polygon-opacity': 'globalAlpha'
15 | };
16 |
17 | function CartoShader(shader) {
18 | this.compiled = {};
19 | this.shader_src = null;
20 | this.compile(shader)
21 | }
22 |
23 | CartoShader.prototype = new VECNIK.Event();
24 |
25 | CartoShader.prototype.compile = function(shader) {
26 | if(typeof shader === 'string') {
27 | shader = eval("(function() { return " + shader +"; })()");
28 | }
29 | this.shader_src = shader;
30 | for(var attr in shader) {
31 | var c = mapper[attr];
32 | if(c) {
33 | this.compiled[c] = eval("(function() { return shader[attr]; })();");
34 | }
35 | }
36 |
37 | this.emit('change');
38 | };
39 |
40 | var needed_settings = {
41 | 'LineString': [
42 | 'line-color',
43 | 'line-width',
44 | 'line-opacity'
45 | ],
46 | 'Polygon': [
47 | 'polygon-fill'
48 | ],
49 | 'MultiPolygon': [
50 | 'polygon-fill'
51 | ]
52 | };
53 | var defaults = {
54 | 'LineString': {
55 | 'strokeStyle': '#000',
56 | 'lineWidth': 1,
57 | 'globalAlpha': 1.0,
58 | 'lineCap': 'round'
59 | },
60 | 'Polygon': {
61 | 'strokeStyle': '#000',
62 | 'lineWidth': 1,
63 | 'globalAlpha': 1.0
64 | },
65 | 'MultiPolygon': {
66 | 'strokeStyle': '#000',
67 | 'lineWidth': 1,
68 | 'globalAlpha': 1.0
69 | }
70 | };
71 |
72 | CartoShader.prototype.needs_render = function(data, render_context, primitive_type) {
73 | var variables = needed_settings[primitive_type];
74 | var shader = this.compiled;
75 | for(var attr in variables) {
76 | var style_attr = variables[attr];
77 | var attr_present = this.shader_src[style_attr];
78 | if(attr_present !== undefined) {
79 | var fn = shader[mapper[style_attr]];
80 | if(typeof fn === 'function') {
81 | fn = fn(data, render_context);
82 | }
83 | if(fn !== null && fn !== undefined) {
84 | return true;
85 | }
86 | }
87 | }
88 | return false;
89 |
90 | }
91 |
92 | CartoShader.prototype.reset = function(ctx, primitive_type) {
93 | var def = defaults[primitive_type];
94 | for(var attr in def) {
95 | ctx[attr] = def[attr];
96 | }
97 | }
98 |
99 | CartoShader.prototype.apply = function(canvas_ctx, data, render_context) {
100 | var shader = this.compiled;
101 | for(var attr in shader) {
102 | var fn = shader[attr];
103 | if(typeof fn === 'function') {
104 | fn = fn(data, render_context);
105 | }
106 | if(fn !== null && canvas_ctx[attr] != fn) {
107 | canvas_ctx[attr] = fn;
108 | }
109 | }
110 | };
111 |
112 | VECNIK.CartoShader = CartoShader;
113 |
114 | })(VECNIK);
115 |
116 | if (typeof module !== 'undefined' && module.exports) {
117 | module.exports.CartoShader = CartoShader;
118 | }
119 |
120 |
121 |
--------------------------------------------------------------------------------
/manhattanhenge/js/vecnik.leatlet.js:
--------------------------------------------------------------------------------
1 | L.TileLayer.Canvas = L.TileLayer.extend({
2 | options: {
3 | async: false
4 | },
5 |
6 | initialize: function (options) {
7 | this.tileSize = tileSize || new MM.Point(256, 256)
8 | this.tiles = new CartoDBSQLAPI({
9 | user: 'vizzuality',
10 | table: 'countries_final',
11 | columns: ['admin'],
12 | });
13 | this.views = new CanvasMapView();
14 | },
15 |
16 | redraw: function () {
17 | },
18 |
19 |
20 | _createTileProto: function () {
21 | var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
22 |
23 | var tileSize = this.options.tileSize;
24 | proto.width = tileSize;
25 | proto.height = tileSize;
26 | },
27 |
28 | _createTile: function () {
29 | var tile = this._canvasProto.cloneNode(false);
30 | tile.onselectstart = tile.onmousemove = L.Util.falseFn;
31 | return tile;
32 | },
33 |
34 | _loadTile: function (tile, tilePoint, zoom) {
35 | tile._layer = this;
36 | tile._tilePoint = tilePoint;
37 | tile._zoom = zoom;
38 |
39 | this.drawTile(tile, tilePoint, zoom);
40 |
41 | if (!this.options.async) {
42 | this.tileDrawn(tile);
43 | }
44 | },
45 | _resetTile: function (tile) {
46 | },
47 |
48 | drawTile: function (tile, tilePoint, zoom) {
49 | // override with rendering code
50 | },
51 |
52 | tileDrawn: function (tile) {
53 | this._tileOnLoad.call(tile);
54 | }
55 | });
56 |
--------------------------------------------------------------------------------
/manhattanhenge/js/vecnik.modestmaps.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // Core
3 | //
4 | // base classes
5 | //========================================
6 |
7 | // create root scope if not exists
8 | var VECNIK = VECNIK || {};
9 |
10 | (function(VECNIK) {
11 | var MM = com.modestmaps;
12 |
13 |
14 | //========================================
15 | // testing provider with mapbox tile layer
16 | //========================================
17 | function TileManagerMapBox() {
18 | }
19 | TileManagerMapBox.prototype = new VECNIK.TileManager();
20 | TileManagerMapBox.prototype.url = function(coordinates) {
21 | return 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-streets/' + coordinates.zoom + '/' + coordinates.row + '/' + coordinates.column + ".png";
22 | }
23 |
24 |
25 | //========================================
26 | // Canvas provider
27 | //========================================
28 | function CanvasProvider(dataSource, shader, renderer, tileSize) {
29 | this.tileSize = tileSize || new MM.Point(256, 256)
30 | this.renderer = renderer;
31 | this.tiles = new VECNIK.TileManager(dataSource)
32 | this.views = new VECNIK.CanvasMapView(shader);
33 | this.shader = shader;
34 | }
35 |
36 | CanvasProvider.prototype.getTile = function(coord) {
37 | var tile = this.tiles.add(coord);
38 | var canvas = new VECNIK.CanvasTileView(tile, this.shader, this.renderer);
39 | this.views.add(canvas);
40 | return canvas.el;
41 | }
42 |
43 | CanvasProvider.prototype.releaseTile = function(coordinates) {
44 | this.tiles.destroy(coordinates);
45 | };
46 |
47 | MM.extend(CanvasProvider, MM.MapProvider);
48 |
49 | VECNIK.MM = {
50 | CanvasProvider: CanvasProvider,
51 | TileManagerMapBox: TileManagerMapBox
52 | };
53 |
54 | })(VECNIK);
55 |
56 |
--------------------------------------------------------------------------------
/manhattanhenge/src/core.js:
--------------------------------------------------------------------------------
1 |
2 | vecnik = window.vecnik || {};
3 |
4 | window.requestAnimFrame = (function(){
5 | return window.requestAnimationFrame ||
6 | window.webkitRequestAnimationFrame ||
7 | window.mozRequestAnimationFrame ||
8 | window.oRequestAnimationFrame ||
9 | window.msRequestAnimationFrame ||
10 | function( callback ){
11 | window.setTimeout(callback, 1000 / 60);
12 | };
13 | })();
14 |
15 | var extend = function(obj, prop) {
16 | for(var p in prop) {
17 | obj[p] = prop[p];
18 | }
19 | }
20 |
21 | vecnik.extend = extend;
22 |
23 | function Event() {}
24 | Event.prototype.on = function(evt, callback) {
25 | var cb = this.callbacks = this.callbacks || {};
26 | var l = cb[evt] || (cb[evt] = []);
27 | l.push(callback);
28 | };
29 |
30 | Event.prototype.emit = function(evt) {
31 | var c = this.callbacks && this.callbacks[evt];
32 | for(var i = 0; c && i < c.length; ++i) {
33 | c[i].apply(this, Array.prototype.slice.call(arguments, 1));
34 | }
35 | };
36 |
37 | vecnik.Events = Event;
38 |
39 | // simple tree implementation
40 | // usage:
41 | // extend(obj, vecnik.Tree)
42 | vecnik.Tree = {
43 |
44 | add: function(node) {
45 | if(!node) return this;
46 | var cb = this.children();
47 | node._parent = this;
48 | cb.push(node);
49 | return this;
50 | },
51 |
52 | remove: function(node) {
53 | var cb = this.children();
54 | if(cb) {
55 | for(var c in cb) {
56 | if(node == cb[c]) {
57 | node._parent = null;
58 | this._children.splice(c, 1);
59 | return;
60 | }
61 | }
62 | }
63 | return this;
64 | },
65 |
66 | children: function() {
67 | return this._children || (this._children = []);
68 | },
69 |
70 | each: function(fn) {
71 | this.children().forEach(fn);
72 | return this;
73 | },
74 |
75 | any: function(fn) {
76 | for(var i in this.children()) {
77 | if(fn(this._children[i]))
78 | return true;
79 | }
80 | return false;
81 | }
82 |
83 | }
84 |
85 | vecnik.timer = (function() {
86 |
87 | function timer(fn) {
88 | if(fn._timer) return;
89 | fn._last = new Date().getTime();
90 | fn._timer = timer;
91 | timer.add(fn);
92 | requestAnimFrame(timer.tick);
93 | }
94 |
95 | timer.tick = function() {
96 | var remove = [];
97 | timer.each(function(n) {
98 | var now = new Date().getTime();
99 | var dt = now - n._last;
100 | if(!n.update(dt)) {
101 | remove.push(n);
102 | }
103 | n._last = now;
104 | });
105 | for(var r in remove) {
106 | var o = remove[r];
107 | timer.remove(o);
108 | o._timer = null;
109 | }
110 | if(timer._children.length) requestAnimFrame(timer.tick);
111 | }
112 |
113 | extend(timer, vecnik.Tree);
114 |
115 | return timer;
116 |
117 | })();
118 |
119 | // http get
120 | vecnik.getJSON = function(url, callback) {
121 | var mygetrequest= new XMLHttpRequest();
122 | mygetrequest.onreadystatechange = function() {
123 | if (mygetrequest.readyState == 4){
124 | if (mygetrequest.status == 200){
125 | callback(JSON.parse(mygetrequest.responseText));
126 | } else {
127 | //error
128 | }
129 | }
130 | };
131 | mygetrequest.open("GET", url, true)
132 | mygetrequest.send(null)
133 | }
134 |
135 | /*
136 | var c = prop({
137 | x: 0,
138 | y: 0
139 | })
140 | c.animate({
141 | x: 1,
142 | y: 1
143 | })
144 | function anim() {
145 |
146 | }
147 |
148 | */
149 |
--------------------------------------------------------------------------------
/manhattanhenge/src/geo.js:
--------------------------------------------------------------------------------
1 |
2 | vecnik.geo = function() {
3 |
4 | var _x, _y, _xt, _yt, _type, _transform;
5 | var _matrix = new mat2();
6 |
7 | // identity
8 | _transform = function(v) { return v; }
9 |
10 | function geo(data) {
11 | }
12 |
13 | function alloc(len) {
14 | _x = new Float32Array(len)
15 | _y = new Float32Array(len)
16 | _xt = new Float32Array(len)
17 | _yt = new Float32Array(len)
18 | }
19 |
20 | function _map(coordinates) {
21 | var c = coordinates;
22 | alloc(c.length);
23 | for(var i = 0; i < c.length; ++i) {
24 | var t = _transform(c[i]);
25 | _x[i] = t[0];
26 | _y[i] = t[1];
27 | }
28 | };
29 |
30 | geo.matrix = function(m) {
31 | if(!arguments.length) return _matrix;
32 | _matrix = m;
33 | this.each(function(g) {
34 | //todo multiply
35 | g.matrix(m);
36 | });
37 | return geo;
38 | }
39 |
40 | geo.x = function() {
41 | var _m = _matrix._m;
42 | for(var i = 0, len = _x.length; i < len; ++i) {
43 | _xt[i] = _m[0]*_x[i] + _m[6];
44 | }
45 | return _xt;
46 | }
47 |
48 | geo.y = function() {
49 | var _m = _matrix._m;
50 | for(var i = 0, len = _y.length; i < len; ++i) {
51 | _yt[i] = _m[1*3 + 1]*_y[i] + _m[7];
52 | }
53 | return _yt;
54 | }
55 |
56 | var conversion = {
57 | Point: function(coordinates) {
58 | alloc(1);
59 | var t = _transform(coordinates);
60 | _x[0] = t[0];
61 | _y[0] = t[1];
62 | },
63 | LineString: _map,
64 | Polygon: function(coordinates) { _map(coordinates[0]); },
65 | MultiPoint: _map,
66 | MultiPolygon: function(polygons) {
67 | for(var i = 0; i < polygons.length; ++i) {
68 | geo.add(vecnik.geo().transform(_transform).parseGeoJSON({
69 | type: 'Polygon',
70 | coordinates: polygons[i]
71 | }));
72 | }
73 | }
74 | };
75 |
76 | geo.parseGeoJSON = function(geometry) {
77 | if(geometry.features) {
78 | for(var i in geometry.features) {
79 | var g = geometry.features[i];
80 | this.add(vecnik.geo().transform(_transform).parseGeoJSON(g));
81 | }
82 | } else {
83 | if(geometry.type === "Feature") {
84 | _metadata = geometry.properties;
85 | geometry = geometry.geometry;
86 | }
87 | _type = geometry.type;
88 | var conversor = conversion[_type];
89 | if(conversor) {
90 | conversor(geometry.coordinates);
91 | }
92 | }
93 | return geo;
94 | }
95 |
96 | geo.transform = function(tr) {
97 | _transform = tr;
98 | return geo;
99 | }
100 |
101 | geo.type = function(t) {
102 | if(!arguments.length) return _type;
103 | _type = t;
104 | return geo;
105 | }
106 |
107 | geo.metadata = function() {
108 | return _metadata;
109 | }
110 |
111 | extend(geo, vecnik.Tree);
112 |
113 | return geo;
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/manhattanhenge/src/latlng.js:
--------------------------------------------------------------------------------
1 |
2 | vecnik.LatLng = function(lat, lng) {
3 | this.x = lng;
4 | this.y = lat;
5 | }
6 |
7 | vecnik.LatLng.prototype = vecnik.vec2(0, 0);
8 |
9 | vecnik.extend(vecnik.LatLng.prototype, {
10 |
11 | lat: function() {
12 | return this.y;
13 | },
14 |
15 | lon: function() {
16 | return this.x;
17 | }
18 |
19 | });
20 |
21 |
--------------------------------------------------------------------------------
/manhattanhenge/src/map.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | function dragger(el) {
4 |
5 | var self = this;
6 | var dragging = false;
7 | var x, y;
8 |
9 | el.ontouchstart = el.onmousedown = function(e) {
10 | dragging = true;
11 | if (e.touches) {
12 | var p = e.touches[0];
13 | x = p.pageX;
14 | y = p.pageY;
15 | } else {
16 | x = e.clientX;
17 | y = e.clientY;
18 | }
19 | self.emit('startdrag', x, y);
20 | };
21 |
22 | el.ontouchmove = el.onmousemove = function(e) {
23 | var xx, yy;
24 | if(!dragging) return;
25 | if (e.touches) {
26 | var p = e.touches[0];
27 | xx = p.pageX;
28 | yy = p.pageY;
29 | } else {
30 | xx = e.clientX;
31 | yy = e.clientY;
32 | }
33 | self.emit('move', xx - x, yy - y);
34 | return false;
35 | };
36 |
37 | el.ontouchend = el.onmouseup = function(e) {
38 | dragging = false;
39 | };
40 | }
41 |
42 | dragger.prototype = new vecnik.Events();
43 |
44 | vecnik.layer = function(size) {
45 | var canvas = this.canvas = document.createElement('canvas');
46 | canvas.style.padding = '0';
47 | canvas.style.margin= '0';
48 | canvas.style.position = 'absolute';
49 | canvas.width = size.x;
50 | canvas.height = size.y;
51 | canvas.style.top = 0;
52 | canvas.style.left = 0;
53 |
54 | var ctx = canvas.getContext( '2d' );
55 |
56 | var _renderer = vecnik.renderer(ctx);
57 |
58 | var translate = {x: 0, y: 0};
59 | var scale = {x: 1, y: 1};
60 |
61 | function layer() { }
62 |
63 | layer.canvas = function() {
64 | return canvas;
65 | }
66 |
67 | layer.renderer = function(r) {
68 | if(!arguments.length) return _renderer;
69 | _renderer = r;
70 | return layer;
71 | }
72 |
73 | layer.translate = function(x, y) {
74 | translate.x = x;
75 | translate.y = y;
76 | return layer;
77 | }
78 |
79 | layer.scale = function(sx, sy) {
80 | scale.x = sx;
81 | scale.y = sy;
82 | }
83 |
84 | layer.clear = function() {
85 | canvas.width = canvas.width;
86 | }
87 |
88 | layer.render = function() {
89 | layer.clear();
90 | ctx.translate(size.x>>1, size.y>>1);
91 | //ctx.scale(scale.x, scale.y);
92 | //ctx.translate(translate.x, translate.y);
93 | this.each(function(geo) {
94 | var m = geo.matrix();
95 | m.scale(scale.x, scale.y)
96 | .translate(translate.x, translate.y);
97 | geo.matrix(m);
98 | _renderer(geo);
99 | });
100 | /*
101 | this.each(function() {
102 | });
103 | ctx.fillRect(-30, -30, 60, 60);
104 | */
105 | }
106 |
107 | extend(layer, vecnik.Tree);
108 | return layer;
109 |
110 | }
111 |
112 | vecnik.map = function(_el) {
113 |
114 | var layers = [];
115 | var center = { x: 0, y: 0};
116 | var zoom = target_zoom = 1;
117 | var el = document.createElement('div');
118 | var width = _el.offsetWidth >> 0;
119 | var height = _el.offsetHeight >> 0;
120 |
121 | _el.appendChild(el);
122 | el.style.padding = '0';
123 | el.style.margin= '0';
124 | el.style.position = 'relative';
125 | el.style.width = width + "px";
126 | el.style.height = height + "px";
127 | el.style.top = 0;
128 | el.style.left = 0;
129 |
130 | function map() {}
131 | extend(map, vecnik.Tree);
132 |
133 | var drag_init = {x:0, y:0};
134 | var target_center = {x: 0, y: 0};
135 | var drag = new dragger(el);
136 | drag.on('startdrag', function() {
137 | drag_init.x = center.x;
138 | drag_init.y = center.y;
139 | });
140 |
141 | el.ondblclick = function(e) {
142 | map.zoom(zoom + 1);
143 | if (e.touches) {
144 | var p = e.touches[0];
145 | x = p.pageX;
146 | y = p.pageY;
147 | } else {
148 | var s = 1.0/Math.pow(2, zoom);
149 | target_center.x = -s*(e.clientX - (width/2)) + center.x;
150 | target_center.y = -s*(e.clientY - (height/2)) + center.y;
151 | }
152 | vecnik.timer(map);
153 | }
154 |
155 | el.onmousewheel = function(e) {
156 | if(e.wheelDeltaY > 0) {
157 | map.zoom(map.zoom() + 0.3);
158 | } else {
159 | map.zoom(map.zoom() - 0.3);
160 | }
161 | }
162 |
163 | drag.on('move', function(dx, dy) {
164 | var s = 1.0/Math.pow(2, zoom);
165 | target_center.x = drag_init.x + dx*s;
166 | target_center.y = drag_init.y + dy*s;
167 | vecnik.timer(map);
168 | });
169 |
170 | map.transform = function(tr) {
171 | transform = tr;
172 | return map;
173 | }
174 |
175 | map.size = function() {
176 | return { x: width, y: height};
177 | }
178 |
179 | map.zoom = function(z) {
180 | if(!arguments.length) return zoom;
181 | target_zoom = z;
182 | vecnik.timer(map);
183 | return map;
184 | }
185 |
186 | map.addLayer = function(layer) {
187 | el.appendChild(layer.canvas());
188 | layers.push(layer);
189 | map.add(layer);
190 | }
191 |
192 | map.update = function(dt) {
193 | var c = center;
194 | var t = target_center;
195 | var dx = t.x - c.x;
196 | var dy = t.y - c.y;
197 | c.x += dx*0.1;
198 | c.y += dy*0.1;
199 |
200 | zoom += (target_zoom - zoom) * 0.01 * dt;
201 |
202 | this.each(function(layer) {
203 | layer.translate(c.x, c.y);
204 | var s = Math.pow(2, zoom);
205 | layer.scale(s, s);
206 | })
207 | map.render();
208 |
209 | return Math.abs(dx) + Math.abs(dy) > 0.001 ||
210 | Math.abs(target_zoom - zoom) > 0.1;
211 | }
212 |
213 | map.render = function() {
214 | this.each(function(layer) {
215 | layer.render();
216 | })
217 | }
218 |
219 | return map;
220 | }
221 |
--------------------------------------------------------------------------------
/manhattanhenge/src/math.js:
--------------------------------------------------------------------------------
1 |
2 | function degreesToRadians(deg) {
3 | return deg * (Math.PI / 180);
4 | }
5 |
6 | function radiansToDegrees(rad) {
7 | return rad / (Math.PI / 180);
8 | }
9 |
10 |
11 | function bound(value, opt_min, opt_max) {
12 | if (opt_min != null) value = Math.max(value, opt_min);
13 | if (opt_max != null) value = Math.min(value, opt_max);
14 | return value;
15 | }
16 |
--------------------------------------------------------------------------------
/manhattanhenge/src/mercator.js:
--------------------------------------------------------------------------------
1 |
2 | //========================================
3 | // Mercator projection
4 | //========================================
5 |
6 | vecnik.mercator = function() {
7 |
8 | var TILE_SIZE = 256;
9 | var pixelOrigin_ = [TILE_SIZE / 2, TILE_SIZE / 2 ];
10 | var pixelsPerLonDegree_ = TILE_SIZE / 360;
11 | var pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);
12 |
13 | function _mercator(latLng, opt_point) {
14 | var me = this;
15 | var point = opt_point || [0, 0];
16 | var origin = pixelOrigin_;
17 |
18 | point[0] = origin[0] + latLng[0] * pixelsPerLonDegree_;
19 |
20 | // NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
21 | // 89.189. This is about a third of a tile past the edge of the world
22 | // tile.
23 | var siny = bound(Math.sin(degreesToRadians(latLng[1])), -0.9999,
24 | 0.9999);
25 | point[1] = origin[1] + 0.5 * Math.log((1 + siny) / (1 - siny)) *
26 | -pixelsPerLonRadian_;
27 | return point;
28 | }
29 |
30 | return _mercator;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/manhattanhenge/src/renderer.canvas.js:
--------------------------------------------------------------------------------
1 |
2 | vecnik.renderer = function(ctx) {
3 |
4 | var _ctx = ctx;
5 | var _shader;
6 |
7 | function render(geo) {
8 | _render(geo);
9 | }
10 |
11 | var self = render;
12 |
13 | var _primitive_render = {
14 |
15 | Point: function(x, y) {
16 | _ctx.save();
17 | var radius = 2;
18 | _ctx.translate(x, y);
19 | _ctx.beginPath();
20 | _ctx.arc(radius, radius, radius, 0, Math.PI * 2, true);
21 | _ctx.closePath();
22 | _ctx.fill();
23 | _ctx.stroke();
24 | _ctx.restore();
25 | },
26 |
27 | MultiPoint: function(x, y) {
28 | var prender = _primitive_render.Point;
29 | for(var i = 0, len = x.length; i < len; ++i) {
30 | prender(x[i], y[i]);
31 | }
32 | },
33 |
34 | Polygon: function(x, y) {
35 | _ctx.beginPath();
36 | _ctx.moveTo(x[0], y[0]);
37 | for(var i = 0, len = x.length; i < len; ++i) {
38 | _ctx.lineTo(x[i], y[i]);
39 | }
40 | _ctx.closePath();
41 | //_ctx.fill();
42 | },
43 |
44 | LineString: function(x, y) {
45 | _ctx.beginPath();
46 | _ctx.moveTo(x[0], y[0]);
47 | for(var i = 0, len = x.length; i < len; ++i) {
48 | _ctx.lineTo(x[i], y[i]);
49 | }
50 | //_ctx.stroke();
51 | }
52 | };
53 |
54 | render.primitiveRender = function(r) {
55 | if(!arguments.length) return _primitive_render;
56 | _primitive_render = r;
57 | return render;
58 | }
59 |
60 | render.shader = function(shader) {
61 | _shader = shader;
62 | }
63 |
64 | function _render(geo) {
65 | var primitive_type;
66 | if(!geo.children().length) {
67 | var primitive_type = geo.type();
68 | var renderer = _primitive_render[primitive_type];
69 | if(renderer) {
70 | var is_active = true;
71 | if(_shader) {
72 | is_active = _shader.needs_render(geo.metadata(), _ctx, primitive_type);
73 | if(is_active) {
74 | _shader.reset(ctx, primitive_type);
75 | _shader.apply(ctx, geo.metadata(), _ctx);
76 | }
77 | }
78 | if (is_active) {
79 | renderer(geo.x(), geo.y());
80 | if(_shader.stroke()) {
81 | _ctx.stroke();
82 | }
83 | if(_shader.fill()) {
84 | _ctx.fill();
85 | }
86 | }
87 | }
88 | } else {
89 | geo.each(_render);
90 | }
91 | };
92 |
93 | return render;
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/manhattanhenge/src/shader.js:
--------------------------------------------------------------------------------
1 | //========================================
2 | // shader
3 | //========================================
4 |
5 | vecnik.shader = function(sh) {
6 |
7 | var mapper = {
8 | 'point-color': 'fillStyle',
9 | 'line-color': 'strokeStyle',
10 | 'line-width': 'lineWidth',
11 | 'line-opacity': 'globalAlpha',
12 | 'polygon-fill': 'fillStyle',
13 | 'polygon-opacity': 'globalAlpha'
14 | };
15 |
16 |
17 | var compiled = {};
18 | var _shader_src = null;
19 |
20 | function _shader(canvas_ctx, data, render_context) {
21 | _shader.apply(canvas_ctx, data, render_context);
22 | }
23 |
24 | //extend(_shader, vecnik.Event);
25 |
26 |
27 | _shader.compile = function(shader) {
28 | if(typeof shader === 'string') {
29 | shader = eval("(function() { return " + shader +"; })()");
30 | }
31 | _shader_src = shader;
32 | for(var attr in shader) {
33 | var c = mapper[attr];
34 | if(c) {
35 | compiled[c] = eval("(function() { return shader[attr]; })();");
36 | }
37 | }
38 |
39 | //_shader.emit('compiled');
40 | return _shader;
41 | };
42 |
43 | var needed_settings = {
44 | 'LineString': [
45 | 'line-color',
46 | 'line-width',
47 | 'line-opacity'
48 | ],
49 | 'Polygon': [
50 | 'polygon-fill',
51 | 'line-width'
52 | ],
53 | 'MultiPolygon': [
54 | 'polygon-fill'
55 | ]
56 | };
57 | var defaults = {
58 | 'LineString': {
59 | 'strokeStyle': '#000',
60 | 'lineWidth': 1,
61 | 'globalAlpha': 1.0,
62 | 'lineCap': 'round'
63 | },
64 | 'Polygon': {
65 | 'strokeStyle': '#000',
66 | 'lineWidth': 1,
67 | 'globalAlpha': 1.0
68 | },
69 | 'MultiPolygon': {
70 | 'strokeStyle': '#000',
71 | 'lineWidth': 1,
72 | 'globalAlpha': 1.0
73 | }
74 | };
75 |
76 | _shader.needs_render = function(data, render_context, primitive_type) {
77 | var variables = needed_settings[primitive_type];
78 | var shader = compiled;
79 | for(var attr in variables) {
80 | var style_attr = variables[attr];
81 | var attr_present = _shader_src[style_attr];
82 | if(attr_present !== undefined) {
83 | var fn = shader[mapper[style_attr]];
84 | if(typeof fn === 'function') {
85 | fn = fn(data, render_context);
86 | }
87 | if(fn !== null && fn !== undefined) {
88 | return true;
89 | }
90 | }
91 | }
92 | return false;
93 | }
94 |
95 | _shader.reset = function(ctx, primitive_type) {
96 | var def = defaults[primitive_type];
97 | for(var attr in def) {
98 | ctx[attr] = def[attr];
99 | }
100 | }
101 |
102 | _shader.apply = function(canvas_ctx, data, render_context) {
103 | var shader = compiled;
104 | for(var attr in shader) {
105 | var fn = shader[attr];
106 | if(typeof fn === 'function') {
107 | fn = fn(data, render_context);
108 | }
109 | if(fn !== null && canvas_ctx[attr] != fn) {
110 | canvas_ctx[attr] = fn;
111 | }
112 | }
113 | };
114 |
115 | _shader.stroke = function() {
116 | return _shader_src['line-width'] != undefined;
117 | }
118 |
119 | _shader.fill = function() {
120 | return _shader_src['polygon-fill'] != undefined;
121 | }
122 |
123 | _shader.compile(sh)
124 |
125 | return _shader;
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/manhattanhenge/src/tween.js:
--------------------------------------------------------------------------------
1 |
2 | function tween() {
3 |
4 | function _tween() {}
5 |
6 | _tween.update = function(dt) {
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/manhattanhenge/src/vec2.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | function _vec2(x, y) {
4 | this.x = x;
5 | this.y = y;
6 | this.length = function() {
7 | return Math.sqrt(this.x*this.x+this.y*this.y);
8 | }
9 | this.len2 = function() {
10 | return this.x*this.x+this.y*this.y;
11 | }
12 | this.add = function(v) {
13 | this.x += v.x;
14 | this.y += v.y;
15 | }
16 | this.clone = function() {
17 | return new vec2(this.x, this.y);
18 | }
19 | }
20 |
21 | var add = function(v1, v2) {
22 | return vec2(v1.x+v2.x, v1.y+v2.y);
23 | }
24 |
25 | var sub = function(v1, v2) {
26 | return vec2(v1.x-v2.x, v1.y-v2.y);
27 | }
28 |
29 | var mul = function(s, v1) {
30 | return vec2(s*v1.x, s*v1.y);
31 | }
32 |
33 | var dot = function(v1, v2) {
34 | return v1.x*v2.x + v1.y*v2.y;
35 | }
36 |
37 | var normalize = function(v1) {
38 | var m = v1.length();
39 | return vec2(v1.x/m, v1.y/m);
40 | }
41 |
42 | function vfn(v, fn) {
43 | return vec2(fn(v.x), fn(v.y));
44 | }
45 |
46 | function normal(v) {
47 | return vec2(-v.y, v.x);
48 | }
49 |
50 | vecnik.vec2 = function vec2(x, y) {
51 | return new _vec2(x, y);
52 | }
53 |
54 | vecnik.extend(vecnik.vec2, {
55 | add: add,
56 | sub: sub,
57 | dot: dot,
58 | mul: mul,
59 | normalize: normalize,
60 | vfn: vfn,
61 | normal: normal
62 | });
63 |
64 |
65 | function mat2(m) {
66 | m = m || [ 1, 0, 0, 0 , 1, 0, 0, 0, 1 ];
67 | this._m = new Float32Array(3*3)
68 | for(var i = 0; i < 3*3; ++i) {
69 | this._m[i] = m[i];
70 | }
71 | }
72 |
73 |
74 | mat2.prototype = {
75 |
76 | identity: function() {
77 | m = [ 1, 0, 0, 0 , 1, 0, 0, 0, 1 ];
78 | for(var i = 0; i < 3*3; ++i) {
79 | this._m[i] = m[i];
80 | }
81 | },
82 |
83 | set: function(i, j, v) {
84 | this._m[j*3 + i] = v;
85 | return this;
86 | },
87 |
88 | get: function(i, j) {
89 | return this._m[j*3 + i];
90 | },
91 |
92 | translate: function(sx, sy) {
93 | return this
94 | .set(0, 2, sx)
95 | .set(1, 2, sy);
96 | },
97 |
98 | scale: function(sx, sy) {
99 | return this
100 | .set(0, 0, sx)
101 | .set(1, 1, sy);
102 | }
103 | }
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/point-clustering/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet multilayer example | CartoDB.js
5 |
6 |
7 |
8 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
43 |
44 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/private-maps/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Private maps
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
94 |
95 |