2014-12-10 01:05:11 +08:00
! function ( e ) { if ( "object" == typeof exports && "undefined" != typeof module ) module . exports = e ( ) ; else if ( "function" == typeof define && define . amd ) define ( [ ] , e ) ; else { var f ; "undefined" != typeof window ? f = window : "undefined" != typeof global ? f = global : "undefined" != typeof self && ( f = self ) , f . torque = e ( ) } } ( function ( ) { var define , module , exports ; return ( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; var f = new Error ( "Cannot find module '" + o + "'" ) ; throw f . code = "MODULE_NOT_FOUND" , f } var l = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( l . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , l , l . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( require , module , exports ) {
( function ( global ) {
var torque = require ( './' ) ;
var requestAnimationFrame = global . requestAnimationFrame
|| global . mozRequestAnimationFrame
|| global . webkitRequestAnimationFrame
|| global . msRequestAnimationFrame
|| function ( callback ) { return global . setTimeout ( callback , 1000 / 60 ) ; } ;
var cancelAnimationFrame = global . cancelAnimationFrame
|| global . mozCancelAnimationFrame
|| global . webkitCancelAnimationFrame
|| global . msCancelAnimationFrame
|| function ( id ) { clearTimeout ( id ) ; } ;
2013-08-06 23:48:25 +08:00
/ * *
* options :
* animationDuration in seconds
* animationDelay in seconds
* /
function Animator ( callback , options ) {
if ( ! options . steps ) {
throw new Error ( "steps option missing" )
}
this . options = options ;
this . running = false ;
this . _tick = this . _tick . bind ( this ) ;
this . _t0 = + new Date ( ) ;
this . callback = callback ;
this . _time = 0.0 ;
2015-01-14 23:51:32 +08:00
this . itemsReady = false ;
2014-12-17 22:34:45 +08:00
2014-12-10 01:05:11 +08:00
this . options = torque . extend ( {
animationDelay : 0 ,
maxDelta : 0.2 ,
2014-12-17 22:34:45 +08:00
loop : options . loop === undefined ? true : options . loop
2014-12-10 01:05:11 +08:00
} , this . options ) ;
2013-08-06 23:48:25 +08:00
2013-10-11 17:09:30 +08:00
this . rescale ( ) ;
2013-08-06 23:48:25 +08:00
}
Animator . prototype = {
start : function ( ) {
2015-01-14 23:51:32 +08:00
this . running = true ;
requestAnimationFrame ( this . _tick ) ;
this . options . onStart && this . options . onStart ( ) ;
2015-03-03 00:35:13 +08:00
if ( this . options . steps === 1 ) {
this . running = false ;
}
2013-08-06 23:48:25 +08:00
} ,
2013-10-29 01:41:27 +08:00
isRunning : function ( ) {
return this . running ;
} ,
2013-08-06 23:48:25 +08:00
stop : function ( ) {
2013-08-29 21:22:57 +08:00
this . pause ( ) ;
2013-10-11 17:09:30 +08:00
this . time ( 0 ) ;
2014-11-21 18:34:37 +08:00
this . options . onStop && this . options . onStop ( ) ;
2013-10-11 17:09:30 +08:00
} ,
// real animation time
time : function ( _ ) {
if ( ! arguments . length ) return this . _time ;
this . _time = _ ;
2013-08-29 21:22:57 +08:00
var t = this . range ( this . domain ( this . _time ) ) ;
this . callback ( t ) ;
} ,
toggle : function ( ) {
if ( this . running ) {
this . pause ( )
} else {
this . start ( )
}
} ,
2013-10-11 17:09:30 +08:00
rescale : function ( ) {
this . domainInv = torque . math . linear ( this . options . animationDelay , this . options . animationDelay + this . options . animationDuration ) ;
this . domain = this . domainInv . invert ( ) ;
this . range = torque . math . linear ( 0 , this . options . steps ) ;
this . rangeInv = this . range . invert ( ) ;
2013-10-29 01:41:27 +08:00
this . time ( this . _time ) ;
2015-03-03 00:35:13 +08:00
this . start ( ) ;
2013-10-11 17:09:30 +08:00
return this ;
} ,
duration : function ( _ ) {
if ( ! arguments . length ) return this . options . animationDuration ;
this . options . animationDuration = _ ;
if ( this . time ( ) > _ ) {
this . time ( 0 ) ;
}
2013-10-29 01:41:27 +08:00
this . rescale ( ) ;
2013-10-11 17:09:30 +08:00
return this ;
} ,
2013-10-29 01:41:27 +08:00
steps : function ( _ ) {
this . options . steps = _ ;
return this . rescale ( ) ;
} ,
2013-09-11 19:21:33 +08:00
step : function ( s ) {
if ( arguments . length === 0 ) return this . range ( this . domain ( this . _time ) ) ;
this . _time = this . domainInv ( this . rangeInv ( s ) ) ;
} ,
2013-08-29 21:22:57 +08:00
pause : function ( ) {
this . running = false ;
cancelAnimationFrame ( this . _tick ) ;
2014-11-21 18:34:37 +08:00
this . options . onPause && this . options . onPause ( ) ;
2013-08-06 23:48:25 +08:00
} ,
_tick : function ( ) {
var t1 = + new Date ( ) ;
var delta = ( t1 - this . _t0 ) * 0.001 ;
2013-08-29 21:22:57 +08:00
// if delta is really big means the tab lost the focus
// at some point, so limit delta change
2013-09-11 19:21:33 +08:00
delta = Math . min ( this . options . maxDelta , delta ) ;
2013-08-06 23:48:25 +08:00
this . _t0 = t1 ;
this . _time += delta ;
2013-10-11 17:09:30 +08:00
if ( this . step ( ) >= this . options . steps ) {
2014-12-17 22:34:45 +08:00
if ( ! this . options . loop ) {
2015-03-02 18:56:25 +08:00
// set time to max time
this . time ( this . options . animationDuration ) ;
this . pause ( ) ;
} else {
this . _time = 0 ;
2014-12-17 22:34:45 +08:00
}
2013-08-29 21:22:57 +08:00
}
if ( this . running ) {
2014-12-17 22:34:45 +08:00
this . time ( this . _time ) ;
2013-08-29 21:22:57 +08:00
requestAnimationFrame ( this . _tick ) ;
}
2013-08-06 23:48:25 +08:00
}
} ;
2014-12-10 01:05:11 +08:00
module . exports = Animator ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { "./" : 10 } ] , 2 : [ function ( require , module , exports ) {
2013-08-01 18:04:39 +08:00
var _torque _reference _latest = {
"version" : "1.0.0" ,
"style" : {
"comp-op" : {
"css" : "comp-op" ,
"default-value" : "src-over" ,
"default-meaning" : "add the current layer on top of other layers" ,
"doc" : "Composite operation. This defines how this layer should behave relative to layers atop or below it." ,
"type" : [
"src" , //
"src-over" , //
"dst-over" , //
"src-in" , //
"dst-in" , //
"src-out" , //
"dst-out" , //
"src-atop" , //
"dst-atop" , //
"xor" , //
"darken" , //
"lighten" //
]
}
} ,
2013-11-23 01:31:04 +08:00
"layer" : {
"buffer-size" : {
"default-value" : "0" ,
"type" : "float" ,
"default-meaning" : "No buffer will be used" ,
"doc" : "Extra tolerance around the Layer extent (in pixels) used to when querying and (potentially) clipping the layer data during rendering"
} ,
2014-12-17 22:34:45 +08:00
"-torque-clear-color" : {
"css" : "-torque-clear-color" ,
"type" : "color" ,
"default-value" : "rgba(255, 255, 255, 0)" ,
"default-meaning" : "full clear" ,
"doc" : "color used to clear canvas on each frame"
} ,
2013-11-25 20:07:34 +08:00
"-torque-frame-count" : {
2014-02-24 21:36:39 +08:00
"css" : "-torque-frame-count" ,
2013-11-23 01:31:04 +08:00
"default-value" : "128" ,
"type" : "number" ,
2013-11-25 20:07:34 +08:00
"default-meaning" : "the data is broken into 128 time frames" ,
"doc" : "Number of animation steps/frames used in the animation. If the data contains a fewere number of total frames, the lesser value will be used."
2013-11-23 01:31:04 +08:00
} ,
"-torque-resolution" : {
2014-02-24 21:36:39 +08:00
"css" : "-torque-resolution" ,
2013-11-23 01:31:04 +08:00
"default-value" : "2" ,
"type" : "number" ,
"default-meaning" : "" ,
"doc" : "Spatial resolution in pixels. A resolution of 1 means no spatial aggregation of the data. Any other resolution of N results in spatial aggregation into cells of NxN pixels. The value N must be power of 2"
} ,
"-torque-animation-duration" : {
2014-02-24 21:36:39 +08:00
"css" : "-torque-animation-duration" ,
2013-11-23 01:31:04 +08:00
"default-value" : "30" ,
"type" : "number" ,
"default-meaning" : "the animation lasts 30 seconds" ,
"doc" : "Animation duration in seconds"
} ,
"-torque-aggregation-function" : {
2014-02-24 21:36:39 +08:00
"css" : "-torque-aggregation-function" ,
2013-11-23 01:31:04 +08:00
"default-value" : "count(cartodb_id)" ,
"type" : "string" ,
"default-meaning" : "the value for each cell is the count of points in that cell" ,
"doc" : "A function used to calculate a value from the aggregate data for each cell. See -torque-resolution"
} ,
"-torque-time-attribute" : {
2014-02-24 21:36:39 +08:00
"css" : "-torque-time-attribute" ,
2013-11-23 01:31:04 +08:00
"default-value" : "time" ,
"type" : "string" ,
"default-meaning" : "the data column in your table that is of a time based type" ,
"doc" : "The table column that contains the time information used create the animation"
} ,
"-torque-data-aggregation" : {
2014-02-24 21:36:39 +08:00
"css" : "-torque-data-aggregation" ,
2013-11-23 01:31:04 +08:00
"default-value" : "linear" ,
"type" : [
"cumulative"
] ,
"default-meaning" : "previous values are discarded" ,
"doc" : "A linear animation will discard previous values while a cumulative animation will accumulate them until it restarts"
}
} ,
2013-08-01 18:04:39 +08:00
"symbolizers" : {
"*" : {
"comp-op" : {
"css" : "comp-op" ,
"default-value" : "src-over" ,
"default-meaning" : "add the current layer on top of other layers" ,
"doc" : "Composite operation. This defines how this layer should behave relative to layers atop or below it." ,
"type" : [
"src" , //
"src-over" , //
"dst-over" , //
"src-in" , //
"dst-in" , //
"src-out" , //
"dst-out" , //
"src-atop" , //
"dst-atop" , //
"xor" , //
"darken" , //
"lighten" //
]
} ,
"opacity" : {
"css" : "opacity" ,
"type" : "float" ,
"doc" : "An alpha value for the style (which means an alpha applied to all features in separate buffer and then composited back to main buffer)" ,
"default-value" : 1 ,
"default-meaning" : "no separate buffer will be used and no alpha will be applied to the style after rendering"
}
} ,
2013-08-29 21:22:57 +08:00
"trail" : {
"steps" : {
"css" : "trail-steps" ,
"type" : "float" ,
"default-value" : 1 ,
"default-meaning" : "no trail steps" ,
"doc" : "How many steps of trails are going to be rendered"
}
} ,
2013-08-01 18:04:39 +08:00
"polygon" : {
"fill" : {
"css" : "polygon-fill" ,
"type" : "color" ,
"default-value" : "rgba(128,128,128,1)" ,
"default-meaning" : "gray and fully opaque (alpha = 1), same as rgb(128,128,128)" ,
"doc" : "Fill color to assign to a polygon"
} ,
"fill-opacity" : {
"css" : "polygon-opacity" ,
"type" : "float" ,
"doc" : "The opacity of the polygon" ,
"default-value" : 1 ,
"default-meaning" : "opaque"
}
} ,
"line" : {
"stroke" : {
"css" : "line-color" ,
"default-value" : "rgba(0,0,0,1)" ,
"type" : "color" ,
"default-meaning" : "black and fully opaque (alpha = 1), same as rgb(0,0,0)" ,
"doc" : "The color of a drawn line"
} ,
"stroke-width" : {
"css" : "line-width" ,
"default-value" : 1 ,
"type" : "float" ,
"doc" : "The width of a line in pixels"
} ,
"stroke-opacity" : {
"css" : "line-opacity" ,
"default-value" : 1 ,
"type" : "float" ,
"default-meaning" : "opaque" ,
"doc" : "The opacity of a line"
} ,
"stroke-linejoin" : {
"css" : "line-join" ,
"default-value" : "miter" ,
"type" : [
"miter" ,
"round" ,
"bevel"
] ,
"doc" : "The behavior of lines when joining"
} ,
"stroke-linecap" : {
"css" : "line-cap" ,
"default-value" : "butt" ,
"type" : [
"butt" ,
"round" ,
"square"
] ,
"doc" : "The display of line endings"
}
} ,
"markers" : {
"file" : {
"css" : "marker-file" ,
"doc" : "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse." ,
"default-value" : "" ,
"default-meaning" : "An ellipse or circle, if width equals height" ,
"type" : "uri"
} ,
"opacity" : {
"css" : "marker-opacity" ,
"doc" : "The overall opacity of the marker, if set, overrides both the opacity of both the fill and stroke" ,
"default-value" : 1 ,
"default-meaning" : "The stroke-opacity and fill-opacity will be used" ,
"type" : "float"
} ,
"fill-opacity" : {
"css" : "marker-fill-opacity" ,
"doc" : "The fill opacity of the marker" ,
"default-value" : 1 ,
"default-meaning" : "opaque" ,
"type" : "float"
} ,
"stroke" : {
"css" : "marker-line-color" ,
"doc" : "The color of the stroke around a marker shape." ,
"default-value" : "black" ,
"type" : "color"
} ,
"stroke-width" : {
"css" : "marker-line-width" ,
"doc" : "The width of the stroke around a marker shape, in pixels. This is positioned on the boundary, so high values can cover the area itself." ,
"type" : "float"
} ,
"stroke-opacity" : {
"css" : "marker-line-opacity" ,
"default-value" : 1 ,
"default-meaning" : "opaque" ,
"doc" : "The opacity of a line" ,
"type" : "float"
} ,
"fill" : {
"css" : "marker-fill" ,
"default-value" : "blue" ,
"doc" : "The color of the area of the marker." ,
"type" : "color"
2013-11-23 01:31:04 +08:00
} ,
"marker-type" : {
"css" : "marker-type" ,
"type" : [
"rectangle" ,
"ellipse"
] ,
"default-value" : "ellipse" ,
"doc" : "The default marker-type. If a SVG file is not given as the marker-file parameter, the renderer provides either an rectangle or an ellipse (a circle if height is equal to width)"
2014-12-19 18:01:04 +08:00
} ,
"width" : {
"css" : "marker-width" ,
"default-value" : 10 ,
"doc" : "The width of the marker, if using one of the default types." ,
"type" : "float"
2013-08-01 18:04:39 +08:00
}
} ,
"point" : {
"file" : {
"css" : "point-file" ,
"type" : "uri" ,
"required" : false ,
"default-value" : "none" ,
"doc" : "Image file to represent a point"
} ,
"opacity" : {
"css" : "point-opacity" ,
"type" : "float" ,
"default-value" : 1.0 ,
"default-meaning" : "Fully opaque" ,
"doc" : "A value from 0 to 1 to control the opacity of the point"
}
}
} ,
"colors" : {
"aliceblue" : [ 240 , 248 , 255 ] ,
"antiquewhite" : [ 250 , 235 , 215 ] ,
"aqua" : [ 0 , 255 , 255 ] ,
"aquamarine" : [ 127 , 255 , 212 ] ,
"azure" : [ 240 , 255 , 255 ] ,
"beige" : [ 245 , 245 , 220 ] ,
"bisque" : [ 255 , 228 , 196 ] ,
"black" : [ 0 , 0 , 0 ] ,
"blanchedalmond" : [ 255 , 235 , 205 ] ,
"blue" : [ 0 , 0 , 255 ] ,
"blueviolet" : [ 138 , 43 , 226 ] ,
"brown" : [ 165 , 42 , 42 ] ,
"burlywood" : [ 222 , 184 , 135 ] ,
"cadetblue" : [ 95 , 158 , 160 ] ,
"chartreuse" : [ 127 , 255 , 0 ] ,
"chocolate" : [ 210 , 105 , 30 ] ,
"coral" : [ 255 , 127 , 80 ] ,
"cornflowerblue" : [ 100 , 149 , 237 ] ,
"cornsilk" : [ 255 , 248 , 220 ] ,
"crimson" : [ 220 , 20 , 60 ] ,
"cyan" : [ 0 , 255 , 255 ] ,
"darkblue" : [ 0 , 0 , 139 ] ,
"darkcyan" : [ 0 , 139 , 139 ] ,
"darkgoldenrod" : [ 184 , 134 , 11 ] ,
"darkgray" : [ 169 , 169 , 169 ] ,
"darkgreen" : [ 0 , 100 , 0 ] ,
"darkgrey" : [ 169 , 169 , 169 ] ,
"darkkhaki" : [ 189 , 183 , 107 ] ,
"darkmagenta" : [ 139 , 0 , 139 ] ,
"darkolivegreen" : [ 85 , 107 , 47 ] ,
"darkorange" : [ 255 , 140 , 0 ] ,
"darkorchid" : [ 153 , 50 , 204 ] ,
"darkred" : [ 139 , 0 , 0 ] ,
"darksalmon" : [ 233 , 150 , 122 ] ,
"darkseagreen" : [ 143 , 188 , 143 ] ,
"darkslateblue" : [ 72 , 61 , 139 ] ,
"darkslategrey" : [ 47 , 79 , 79 ] ,
"darkturquoise" : [ 0 , 206 , 209 ] ,
"darkviolet" : [ 148 , 0 , 211 ] ,
"deeppink" : [ 255 , 20 , 147 ] ,
"deepskyblue" : [ 0 , 191 , 255 ] ,
"dimgray" : [ 105 , 105 , 105 ] ,
"dimgrey" : [ 105 , 105 , 105 ] ,
"dodgerblue" : [ 30 , 144 , 255 ] ,
"firebrick" : [ 178 , 34 , 34 ] ,
"floralwhite" : [ 255 , 250 , 240 ] ,
"forestgreen" : [ 34 , 139 , 34 ] ,
"fuchsia" : [ 255 , 0 , 255 ] ,
"gainsboro" : [ 220 , 220 , 220 ] ,
"ghostwhite" : [ 248 , 248 , 255 ] ,
"gold" : [ 255 , 215 , 0 ] ,
"goldenrod" : [ 218 , 165 , 32 ] ,
"gray" : [ 128 , 128 , 128 ] ,
"grey" : [ 128 , 128 , 128 ] ,
"green" : [ 0 , 128 , 0 ] ,
"greenyellow" : [ 173 , 255 , 47 ] ,
"honeydew" : [ 240 , 255 , 240 ] ,
"hotpink" : [ 255 , 105 , 180 ] ,
"indianred" : [ 205 , 92 , 92 ] ,
"indigo" : [ 75 , 0 , 130 ] ,
"ivory" : [ 255 , 255 , 240 ] ,
"khaki" : [ 240 , 230 , 140 ] ,
"lavender" : [ 230 , 230 , 250 ] ,
"lavenderblush" : [ 255 , 240 , 245 ] ,
"lawngreen" : [ 124 , 252 , 0 ] ,
"lemonchiffon" : [ 255 , 250 , 205 ] ,
"lightblue" : [ 173 , 216 , 230 ] ,
"lightcoral" : [ 240 , 128 , 128 ] ,
"lightcyan" : [ 224 , 255 , 255 ] ,
"lightgoldenrodyellow" : [ 250 , 250 , 210 ] ,
"lightgray" : [ 211 , 211 , 211 ] ,
"lightgreen" : [ 144 , 238 , 144 ] ,
"lightgrey" : [ 211 , 211 , 211 ] ,
"lightpink" : [ 255 , 182 , 193 ] ,
"lightsalmon" : [ 255 , 160 , 122 ] ,
"lightseagreen" : [ 32 , 178 , 170 ] ,
"lightskyblue" : [ 135 , 206 , 250 ] ,
"lightslategray" : [ 119 , 136 , 153 ] ,
"lightslategrey" : [ 119 , 136 , 153 ] ,
"lightsteelblue" : [ 176 , 196 , 222 ] ,
"lightyellow" : [ 255 , 255 , 224 ] ,
"lime" : [ 0 , 255 , 0 ] ,
"limegreen" : [ 50 , 205 , 50 ] ,
"linen" : [ 250 , 240 , 230 ] ,
"magenta" : [ 255 , 0 , 255 ] ,
"maroon" : [ 128 , 0 , 0 ] ,
"mediumaquamarine" : [ 102 , 205 , 170 ] ,
"mediumblue" : [ 0 , 0 , 205 ] ,
"mediumorchid" : [ 186 , 85 , 211 ] ,
"mediumpurple" : [ 147 , 112 , 219 ] ,
"mediumseagreen" : [ 60 , 179 , 113 ] ,
"mediumslateblue" : [ 123 , 104 , 238 ] ,
"mediumspringgreen" : [ 0 , 250 , 154 ] ,
"mediumturquoise" : [ 72 , 209 , 204 ] ,
"mediumvioletred" : [ 199 , 21 , 133 ] ,
"midnightblue" : [ 25 , 25 , 112 ] ,
"mintcream" : [ 245 , 255 , 250 ] ,
"mistyrose" : [ 255 , 228 , 225 ] ,
"moccasin" : [ 255 , 228 , 181 ] ,
"navajowhite" : [ 255 , 222 , 173 ] ,
"navy" : [ 0 , 0 , 128 ] ,
"oldlace" : [ 253 , 245 , 230 ] ,
"olive" : [ 128 , 128 , 0 ] ,
"olivedrab" : [ 107 , 142 , 35 ] ,
"orange" : [ 255 , 165 , 0 ] ,
"orangered" : [ 255 , 69 , 0 ] ,
"orchid" : [ 218 , 112 , 214 ] ,
"palegoldenrod" : [ 238 , 232 , 170 ] ,
"palegreen" : [ 152 , 251 , 152 ] ,
"paleturquoise" : [ 175 , 238 , 238 ] ,
"palevioletred" : [ 219 , 112 , 147 ] ,
"papayawhip" : [ 255 , 239 , 213 ] ,
"peachpuff" : [ 255 , 218 , 185 ] ,
"peru" : [ 205 , 133 , 63 ] ,
"pink" : [ 255 , 192 , 203 ] ,
"plum" : [ 221 , 160 , 221 ] ,
"powderblue" : [ 176 , 224 , 230 ] ,
"purple" : [ 128 , 0 , 128 ] ,
"red" : [ 255 , 0 , 0 ] ,
"rosybrown" : [ 188 , 143 , 143 ] ,
"royalblue" : [ 65 , 105 , 225 ] ,
"saddlebrown" : [ 139 , 69 , 19 ] ,
"salmon" : [ 250 , 128 , 114 ] ,
"sandybrown" : [ 244 , 164 , 96 ] ,
"seagreen" : [ 46 , 139 , 87 ] ,
"seashell" : [ 255 , 245 , 238 ] ,
"sienna" : [ 160 , 82 , 45 ] ,
"silver" : [ 192 , 192 , 192 ] ,
"skyblue" : [ 135 , 206 , 235 ] ,
"slateblue" : [ 106 , 90 , 205 ] ,
"slategray" : [ 112 , 128 , 144 ] ,
"slategrey" : [ 112 , 128 , 144 ] ,
"snow" : [ 255 , 250 , 250 ] ,
"springgreen" : [ 0 , 255 , 127 ] ,
"steelblue" : [ 70 , 130 , 180 ] ,
"tan" : [ 210 , 180 , 140 ] ,
"teal" : [ 0 , 128 , 128 ] ,
"thistle" : [ 216 , 191 , 216 ] ,
"tomato" : [ 255 , 99 , 71 ] ,
"turquoise" : [ 64 , 224 , 208 ] ,
"violet" : [ 238 , 130 , 238 ] ,
"wheat" : [ 245 , 222 , 179 ] ,
"white" : [ 255 , 255 , 255 ] ,
"whitesmoke" : [ 245 , 245 , 245 ] ,
"yellow" : [ 255 , 255 , 0 ] ,
"yellowgreen" : [ 154 , 205 , 50 ] ,
"transparent" : [ 0 , 0 , 0 , 0 ]
}
} ;
2014-12-10 01:05:11 +08:00
module . exports = {
2013-08-01 18:04:39 +08:00
version : {
latest : _torque _reference _latest ,
'1.0.0' : _torque _reference _latest
}
2014-12-10 01:05:11 +08:00
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} , { } ] , 3 : [ function ( require , module , exports ) {
( function ( global ) {
2013-11-23 01:31:04 +08:00
//
// common functionallity for torque layers
//
2014-12-10 01:05:11 +08:00
var carto = global . carto || require ( 'carto' ) ;
2013-11-23 01:31:04 +08:00
function TorqueLayer ( ) { }
TorqueLayer . prototype = {
} ;
TorqueLayer . optionsFromLayer = function ( mapConfig ) {
var opts = { } ;
if ( ! mapConfig ) return opts ;
var attrs = {
'buffer-size' : 'buffer-size' ,
2013-11-25 20:07:34 +08:00
'-torque-frame-count' : 'steps' ,
2013-11-23 01:31:04 +08:00
'-torque-resolution' : 'resolution' ,
'-torque-animation-duration' : 'animationDuration' ,
'-torque-aggregation-function' : 'countby' ,
'-torque-time-attribute' : 'column' ,
'-torque-data-aggregation' : 'data_aggregation'
} ;
for ( var i in attrs ) {
var v = mapConfig . eval ( i ) ;
if ( v !== undefined ) {
var a = attrs [ i ] ;
opts [ a ] = v ;
}
}
return opts ;
} ;
TorqueLayer . optionsFromCartoCSS = function ( cartocss ) {
var shader = new carto . RendererJS ( ) . render ( cartocss ) ;
var mapConfig = shader . findLayer ( { name : 'Map' } ) ;
return TorqueLayer . optionsFromLayer ( mapConfig ) ;
} ;
2014-12-10 01:05:11 +08:00
module . exports . TorqueLayer = TorqueLayer ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { "carto" : undefined } ] , 4 : [ function ( require , module , exports ) {
( function ( global ) {
2013-09-11 19:21:33 +08:00
var Event = { } ;
Event . on = function ( evt , callback ) {
var cb = this . _evt _callbacks = this . _evt _callbacks || { } ;
var l = cb [ evt ] || ( cb [ evt ] = [ ] ) ;
l . push ( callback ) ;
2014-07-07 20:51:01 +08:00
return this ;
2013-09-11 19:21:33 +08:00
} ;
Event . trigger = function ( evt ) {
var c = this . _evt _callbacks && this . _evt _callbacks [ evt ] ;
for ( var i = 0 ; c && i < c . length ; ++ i ) {
c [ i ] . apply ( this , Array . prototype . slice . call ( arguments , 1 ) ) ;
}
2014-07-07 20:51:01 +08:00
return this ;
2013-09-11 19:21:33 +08:00
} ;
Event . fire = Event . trigger ;
Event . off = function ( evt , callback ) {
var c = this . _evt _callbacks && this . _evt _callbacks [ evt ] ;
if ( c && ! callback ) {
delete this . _evt _callbacks [ evt ] ;
return this ;
}
var remove = [ ] ;
for ( var i = 0 ; c && i < c . length ; ++ i ) {
if ( c [ i ] === callback ) remove . push ( i ) ;
}
2013-10-29 01:41:27 +08:00
while ( ( i = remove . pop ( ) ) !== undefined ) c . splice ( i , 1 ) ;
2014-07-07 20:51:01 +08:00
return this ;
2013-10-29 01:41:27 +08:00
} ;
Event . callbacks = function ( evt ) {
return ( this . _evt _callbacks && this . _evt _callbacks [ evt ] ) || [ ] ;
2013-09-11 19:21:33 +08:00
} ;
2014-12-10 01:05:11 +08:00
function extend ( ) {
var objs = arguments ;
var a = objs [ 0 ] ;
for ( var i = 1 ; i < objs . length ; ++ i ) {
var b = objs [ i ] ;
for ( var k in b ) {
a [ k ] = b [ k ] ;
}
}
return a ;
2014-07-07 20:51:01 +08:00
}
2014-12-10 01:05:11 +08:00
function clone ( a ) {
return extend ( { } , a ) ;
2014-07-07 20:51:01 +08:00
}
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
function isFunction ( f ) {
2014-10-21 23:16:23 +08:00
return typeof f == 'function' || false ;
}
2014-12-10 01:05:11 +08:00
function isArray ( value ) {
2014-07-07 20:51:01 +08:00
return value && typeof value == 'object' && Object . prototype . toString . call ( value ) == '[object Array]' ;
2014-12-10 01:05:11 +08:00
}
2013-09-11 19:21:33 +08:00
// types
2014-12-10 01:05:11 +08:00
var types = {
Uint8Array : typeof ( global [ 'Uint8Array' ] ) !== 'undefined' ? global . Uint8Array : Array ,
Uint32Array : typeof ( global [ 'Uint32Array' ] ) !== 'undefined' ? global . Uint32Array : Array ,
Int32Array : typeof ( global [ 'Int32Array' ] ) !== 'undefined' ? global . Int32Array : Array
2013-11-05 15:57:40 +08:00
} ;
2014-12-10 01:05:11 +08:00
function isBrowserSupported ( ) {
2013-11-05 15:57:40 +08:00
return ! ! document . createElement ( 'canvas' ) ;
2014-10-21 23:16:23 +08:00
}
2014-12-10 01:05:11 +08:00
function userAgent ( ) {
return typeof navigator !== 'undefined' ? navigator . userAgent : '' ;
}
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var flags = {
sprites _to _images : userAgent ( ) . indexOf ( 'Safari' ) === - 1
2014-02-24 21:36:39 +08:00
} ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
module . exports = {
Event : Event ,
extend : extend ,
clone : clone ,
isFunction : isFunction ,
isArray : isArray ,
types : types ,
isBrowserSupported : isBrowserSupported ,
flags : flags
} ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { } ] , 5 : [ function ( require , module , exports ) {
/ * *
* @ license
* Copyright 2013 Google Inc . All Rights Reserved .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* @ fileoverview Extends OverlayView to provide a canvas "Layer" .
* @ author Brendan Kenny
* /
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A map layer that provides a canvas over the slippy map and a callback
* system for efficient animation . Requires canvas and CSS 2 D transform
* support .
* @ constructor
* @ extends google . maps . OverlayView
* @ param { CanvasLayerOptions = } opt _options Options to set in this CanvasLayer .
* /
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
function CanvasLayer ( opt _options ) {
/ * *
* If true , canvas is in a map pane and the OverlayView is fully functional .
* See google . maps . OverlayView . onAdd for more information .
* @ type { boolean }
* @ private
* /
this . isAdded _ = false ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* If true , each update will immediately schedule the next .
* @ type { boolean }
* @ private
* /
this . isAnimated _ = false ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The name of the MapPane in which this layer will be displayed .
* @ type { string }
* @ private
* /
this . paneName _ = CanvasLayer . DEFAULT _PANE _NAME _ ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A user - supplied function called whenever an update is required . Null or
* undefined if a callback is not provided .
* @ type { ? function = }
* @ private
* /
this . updateHandler _ = null ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A user - supplied function called whenever an update is required and the
* map has been resized since the last update . Null or undefined if a
* callback is not provided .
* @ type { ? function }
* @ private
* /
this . resizeHandler _ = null ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The LatLng coordinate of the top left of the current view of the map . Will
* be null when this . isAdded _ is false .
* @ type { google . maps . LatLng }
* @ private
* /
this . topLeft _ = null ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The map - pan event listener . Will be null when this . isAdded _ is false . Will
* be null when this . isAdded _ is false .
* @ type { ? function }
* @ private
* /
this . centerListener _ = null ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The map - resize event listener . Will be null when this . isAdded _ is false .
* @ type { ? function }
* @ private
* /
this . resizeListener _ = null ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* If true , the map size has changed and this . resizeHandler _ must be called
* on the next update .
* @ type { boolean }
* @ private
* /
this . needsResize _ = true ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A browser - defined id for the currently requested callback . Null when no
* callback is queued .
* @ type { ? number }
* @ private
* /
this . requestAnimationFrameId _ = null ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
var canvas = document . createElement ( 'canvas' ) ;
canvas . style . position = 'absolute' ;
canvas . style . top = 0 ;
canvas . style . left = 0 ;
canvas . style . pointerEvents = 'none' ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The canvas element .
* @ type { ! HTMLCanvasElement }
* /
this . canvas = canvas ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Simple bind for functions with no args for bind - less browsers ( Safari ) .
* @ param { Object } thisArg The this value used for the target function .
* @ param { function } func The function to be bound .
* /
function simpleBindShim ( thisArg , func ) {
return function ( ) { func . apply ( thisArg ) ; } ;
}
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A reference to this . repositionCanvas _ with this bound as its this value .
* @ type { function }
* @ private
* /
this . repositionFunction _ = simpleBindShim ( this , this . repositionCanvas _ ) ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A reference to this . resize _ with this bound as its this value .
* @ type { function }
* @ private
* /
this . resizeFunction _ = simpleBindShim ( this , this . resize _ ) ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A reference to this . update _ with this bound as its this value .
* @ type { function }
* @ private
* /
this . requestUpdateFunction _ = simpleBindShim ( this , this . update _ ) ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
// set provided options, if any
if ( opt _options ) {
this . setOptions ( opt _options ) ;
}
}
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
CanvasLayer . prototype = new google . maps . OverlayView ( ) ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The default MapPane to contain the canvas .
* @ type { string }
* @ const
* @ private
* /
CanvasLayer . DEFAULT _PANE _NAME _ = 'overlayLayer' ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Transform CSS property name , with vendor prefix if required . If browser
* does not support transforms , property will be ignored .
* @ type { string }
* @ const
* @ private
* /
CanvasLayer . CSS _TRANSFORM _ = ( function ( ) {
var div = document . createElement ( 'div' ) ;
var transformProps = [
'transform' ,
'WebkitTransform' ,
'MozTransform' ,
'OTransform' ,
'msTransform'
] ;
for ( var i = 0 ; i < transformProps . length ; i ++ ) {
var prop = transformProps [ i ] ;
if ( div . style [ prop ] !== undefined ) {
return prop ;
}
}
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
// return unprefixed version by default
return transformProps [ 0 ] ;
} ) ( ) ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The requestAnimationFrame function , with vendor - prefixed or setTimeout - based
* fallbacks . MUST be called with window as thisArg .
* @ type { function }
* @ param { function } callback The function to add to the frame request queue .
* @ return { number } The browser - defined id for the requested callback .
* @ private
* /
CanvasLayer . prototype . requestAnimFrame _ =
window . requestAnimationFrame ||
window . webkitRequestAnimationFrame ||
window . mozRequestAnimationFrame ||
window . oRequestAnimationFrame ||
window . msRequestAnimationFrame ||
function ( callback ) {
return window . setTimeout ( callback , 1000 / 60 ) ;
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The cancelAnimationFrame function , with vendor - prefixed fallback . Does not
* fall back to clearTimeout as some platforms implement requestAnimationFrame
* but not cancelAnimationFrame , and the cost is an extra frame on onRemove .
* MUST be called with window as thisArg .
* @ type { function }
* @ param { number = } requestId The id of the frame request to cancel .
* @ private
* /
CanvasLayer . prototype . cancelAnimFrame _ =
window . cancelAnimationFrame ||
window . webkitCancelAnimationFrame ||
window . mozCancelAnimationFrame ||
window . oCancelAnimationFrame ||
window . msCancelAnimationFrame ||
function ( requestId ) { } ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Sets any options provided . See CanvasLayerOptions for more information .
* @ param { CanvasLayerOptions } options The options to set .
* /
CanvasLayer . prototype . setOptions = function ( options ) {
if ( options . animate !== undefined ) {
this . setAnimate ( options . animate ) ;
}
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
if ( options . paneName !== undefined ) {
this . setPane ( options . paneName ) ;
2013-08-01 18:04:39 +08:00
}
2014-12-10 01:05:11 +08:00
if ( options . updateHandler !== undefined ) {
this . setUpdateHandler ( options . updateHandler ) ;
}
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
if ( options . resizeHandler !== undefined ) {
this . setResizeHandler ( options . resizeHandler ) ;
}
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
if ( options . readyHandler ) {
this . readyHandler = options . readyHandler ;
}
2013-08-01 18:04:39 +08:00
2013-07-30 16:45:35 +08:00
} ;
2014-12-10 01:05:11 +08:00
/ * *
* Set the animated state of the layer . If true , updateHandler will be called
* repeatedly , once per frame . If false , updateHandler will only be called when
* a map property changes that could require the canvas content to be redrawn .
* @ param { boolean } animate Whether the canvas is animated .
* /
CanvasLayer . prototype . setAnimate = function ( animate ) {
this . isAnimated _ = ! ! animate ;
if ( this . isAnimated _ ) {
this . scheduleUpdate ( ) ;
2013-08-01 18:04:39 +08:00
}
} ;
2014-12-10 01:05:11 +08:00
/ * *
* @ return { boolean } Whether the canvas is animated .
* /
CanvasLayer . prototype . isAnimated = function ( ) {
return this . isAnimated _ ;
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Set the MapPane in which this layer will be displayed , by name . See
* { @ code google . maps . MapPanes } for the panes available .
* @ param { string } paneName The name of the desired MapPane .
* /
CanvasLayer . prototype . setPaneName = function ( paneName ) {
this . paneName _ = paneName ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
this . setPane _ ( ) ;
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* @ return { string } The name of the current container pane .
* /
CanvasLayer . prototype . getPaneName = function ( ) {
return this . paneName _ ;
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Adds the canvas to the specified container pane . Since this is guaranteed to
* execute only after onAdd is called , this is when paneName ' s existence is
* checked ( and an error is thrown if it doesn ' t exist ) .
* @ private
* /
CanvasLayer . prototype . setPane _ = function ( ) {
if ( ! this . isAdded _ ) {
return ;
}
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
// onAdd has been called, so panes can be used
var panes = this . getPanes ( ) ;
if ( ! panes [ this . paneName _ ] ) {
throw new Error ( '"' + this . paneName _ + '" is not a valid MapPane name.' ) ;
2013-08-01 18:04:39 +08:00
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
panes [ this . paneName _ ] . appendChild ( this . canvas ) ;
2013-07-30 16:45:35 +08:00
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Set a function that will be called whenever the parent map and the overlay ' s
* canvas have been resized . If opt _resizeHandler is null or unspecified , any
* existing callback is removed .
* @ param { ? function = } opt _resizeHandler The resize callback function .
* /
CanvasLayer . prototype . setResizeHandler = function ( opt _resizeHandler ) {
this . resizeHandler _ = opt _resizeHandler ;
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Set a function that will be called when a repaint of the canvas is required .
* If opt _updateHandler is null or unspecified , any existing callback is
* removed .
* @ param { ? function = } opt _updateHandler The update callback function .
* /
CanvasLayer . prototype . setUpdateHandler = function ( opt _updateHandler ) {
this . updateHandler _ = opt _updateHandler ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* @ inheritDoc
* /
CanvasLayer . prototype . onAdd = function ( ) {
if ( this . isAdded _ ) {
return ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . isAdded _ = true ;
this . setPane _ ( ) ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
this . resizeListener _ = google . maps . event . addListener ( this . getMap ( ) ,
'resize' , this . resizeFunction _ ) ;
this . centerListener _ = google . maps . event . addListener ( this . getMap ( ) ,
'center_changed' , this . repositionFunction _ ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . resize _ ( ) ;
this . repositionCanvas _ ( ) ;
this . readyHandler && this . readyHandler ( ) ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* @ inheritDoc
* /
CanvasLayer . prototype . onRemove = function ( ) {
if ( ! this . isAdded _ ) {
return ;
}
2013-09-28 00:56:03 +08:00
2014-12-10 01:05:11 +08:00
this . isAdded _ = false ;
this . topLeft _ = null ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// remove canvas and listeners for pan and resize from map
this . canvas . parentElement . removeChild ( this . canvas ) ;
if ( this . centerListener _ ) {
google . maps . event . removeListener ( this . centerListener _ ) ;
this . centerListener _ = null ;
}
if ( this . resizeListener _ ) {
google . maps . event . removeListener ( this . resizeListener _ ) ;
this . resizeListener _ = null ;
}
2013-09-28 00:56:03 +08:00
2014-12-10 01:05:11 +08:00
// cease canvas update callbacks
if ( this . requestAnimationFrameId _ ) {
this . cancelAnimFrame _ . call ( window , this . requestAnimationFrameId _ ) ;
this . requestAnimationFrameId _ = null ;
}
} ;
2013-09-28 00:56:03 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* The internal callback for resize events that resizes the canvas to keep the
* map properly covered .
* @ private
* /
CanvasLayer . prototype . resize _ = function ( ) {
// TODO(bckenny): it's common to use a smaller canvas but use CSS to scale
// what is drawn by the browser to save on fill rate. Add an option to do
// this.
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( ! this . isAdded _ ) {
return ;
}
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
var map = this . getMap ( ) ;
var width = map . getDiv ( ) . offsetWidth ;
var height = map . getDiv ( ) . offsetHeight ;
var oldWidth = this . canvas . width ;
var oldHeight = this . canvas . height ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// resizing may allocate a new back buffer, so do so conservatively
if ( oldWidth !== width || oldHeight !== height ) {
this . canvas . width = width ;
this . canvas . height = height ;
this . canvas . style . width = width + 'px' ;
this . canvas . style . height = height + 'px' ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
this . needsResize _ = true ;
this . scheduleUpdate ( ) ;
}
} ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* @ inheritDoc
* /
CanvasLayer . prototype . draw = function ( ) {
this . repositionCanvas _ ( ) ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Internal callback for map view changes . Since the Maps API moves the overlay
* along with the map , this function calculates the opposite translation to
* keep the canvas in place .
* @ private
* /
CanvasLayer . prototype . repositionCanvas _ = function ( ) {
// TODO(bckenny): *should* only be executed on RAF, but in current browsers
// this causes noticeable hitches in map and overlay relative
// positioning.
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
var bounds = this . getMap ( ) . getBounds ( ) ;
this . topLeft _ = new google . maps . LatLng ( bounds . getNorthEast ( ) . lat ( ) ,
bounds . getSouthWest ( ) . lng ( ) ) ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// canvas position relative to draggable map's conatainer depends on
// overlayView's projection, not the map's
var projection = this . getProjection ( ) ;
var divTopLeft = projection . fromLatLngToDivPixel ( this . topLeft _ ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// when the zoom level is low, more than one map can be shown in the screen
// so the canvas should be attach to the map with more are in the screen
var mapSize = ( 1 << this . getMap ( ) . getZoom ( ) ) * 256 ;
if ( Math . abs ( divTopLeft . x ) > mapSize ) {
divTopLeft . x -= mapSize ;
}
this . canvas . style [ CanvasLayer . CSS _TRANSFORM _ ] = 'translate(' +
Math . round ( divTopLeft . x ) + 'px,' + Math . round ( divTopLeft . y ) + 'px)' ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . scheduleUpdate ( ) ;
} ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Internal callback that serves as main animation scheduler via
* requestAnimationFrame . Calls resize and update callbacks if set , and
* schedules the next frame if overlay is animated .
* @ private
* /
CanvasLayer . prototype . update _ = function ( ) {
this . requestAnimationFrameId _ = null ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
if ( ! this . isAdded _ ) {
return ;
}
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
if ( this . isAnimated _ ) {
this . scheduleUpdate ( ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( this . needsResize _ && this . resizeHandler _ ) {
this . needsResize _ = false ;
this . resizeHandler _ ( ) ;
}
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
if ( this . updateHandler _ ) {
this . updateHandler _ ( ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* A convenience method to get the current LatLng coordinate of the top left of
* the current view of the map .
* @ return { google . maps . LatLng } The top left coordinate .
* /
CanvasLayer . prototype . getTopLeft = function ( ) {
return this . topLeft _ ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* Schedule a requestAnimationFrame callback to updateHandler . If one is
* already scheduled , there is no effect .
* /
CanvasLayer . prototype . scheduleUpdate = function ( ) {
if ( this . isAdded _ && ! this . requestAnimationFrameId _ ) {
this . requestAnimationFrameId _ =
this . requestAnimFrame _ . call ( window , this . requestUpdateFunction _ ) ;
}
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
module . exports = CanvasLayer ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} , { } ] , 6 : [ function ( require , module , exports ) {
/ *
=== === === === === === ==
canvas setup for drawing tiles
=== === === === === === ==
* /
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
function CanvasTileLayer ( canvas _setup , render ) {
this . tileSize = new google . maps . Size ( 256 , 256 ) ;
this . maxZoom = 19 ;
this . name = "Tile #s" ;
this . alt = "Canvas tile layer" ;
this . tiles = { } ;
this . canvas _setup = canvas _setup ;
this . render = render ;
if ( ! render ) {
this . render = canvas _setup ;
}
}
2013-09-11 19:21:33 +08:00
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
// create a tile with a canvas element
CanvasTileLayer . prototype . create _tile _canvas = function ( coord , zoom , ownerDocument ) {
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// create canvas and reset style
var canvas = ownerDocument . createElement ( 'canvas' ) ;
var hit _canvas = ownerDocument . createElement ( 'canvas' ) ;
canvas . style . border = hit _canvas . style . border = "none" ;
canvas . style . margin = hit _canvas . style . margin = "0" ;
canvas . style . padding = hit _canvas . style . padding = "0" ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// prepare canvas and context sizes
var ctx = canvas . getContext ( '2d' ) ;
ctx . width = canvas . width = this . tileSize . width ;
ctx . height = canvas . height = this . tileSize . height ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var hit _ctx = hit _canvas . getContext ( '2d' ) ;
hit _canvas . width = hit _ctx . width = this . tileSize . width ;
hit _canvas . height = hit _ctx . height = this . tileSize . height ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
//set unique id
var tile _id = coord . x + '_' + coord . y + '_' + zoom ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
canvas . setAttribute ( 'id' , tile _id ) ;
hit _canvas . setAttribute ( 'id' , tile _id ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( tile _id in this . tiles )
delete this . tiles [ tile _id ] ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . tiles [ tile _id ] = { canvas : canvas , ctx : ctx , hit _canvas : hit _canvas , hit _ctx : hit _ctx , coord : coord , zoom : zoom , primitives : null } ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// custom setup
//if (tile_id == '19295_24654_16'){
if ( this . canvas _setup )
this . canvas _setup ( this . tiles [ tile _id ] , coord , zoom ) ;
//}
return canvas ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
CanvasTileLayer . prototype . each = function ( callback ) {
for ( var t in this . tiles ) {
var tile = this . tiles [ t ] ;
callback ( tile ) ;
}
}
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
CanvasTileLayer . prototype . recreate = function ( ) {
for ( var t in this . tiles ) {
var tile = this . tiles [ t ] ;
this . canvas _setup ( tile , tile . coord , tile . zoom ) ;
}
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
CanvasTileLayer . prototype . redraw _tile = function ( tile ) {
this . render ( tile , tile . coord , tile . zoom ) ;
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
CanvasTileLayer . prototype . redraw = function ( ) {
for ( var t in this . tiles ) {
var tile = this . tiles [ t ] ;
this . render ( tile , tile . coord , tile . zoom ) ;
}
} ;
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
// could be called directly...
CanvasTileLayer . prototype . getTile = function ( coord , zoom , ownerDocument ) {
return this . create _tile _canvas ( coord , zoom , ownerDocument ) ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
CanvasTileLayer . prototype . releaseTile = function ( tile ) {
var id = tile . getAttribute ( 'id' ) ;
delete this . tiles [ id ] ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
module . exports = CanvasTileLayer ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} , { } ] , 7 : [ function ( require , module , exports ) {
function GMapsTileLoader ( ) {
}
GMapsTileLoader . prototype = {
_initTileLoader : function ( map , projection ) {
this . _map = map ;
this . _projection = projection ;
this . _tiles = { } ;
this . _tilesLoading = { } ;
this . _tilesToLoad = 0 ;
this . _updateTiles = this . _updateTiles . bind ( this ) ;
this . _listeners = [ ] ;
this . _listeners . push (
google . maps . event . addListener ( this . _map , 'dragend' , this . _updateTiles ) ,
google . maps . event . addListener ( this . _map , 'zoom_changed' , this . _updateTiles )
) ;
this . tileSize = 256 ;
this . _updateTiles ( ) ;
} ,
_removeTileLoader : function ( ) {
for ( var i in this . _listeners ) {
google . maps . event . removeListener ( this . _listeners [ i ] ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
this . _removeTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_removeTiles : function ( ) {
for ( var key in this . _tiles ) {
this . _removeTile ( key ) ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_reloadTiles : function ( ) {
this . _removeTiles ( ) ;
this . _updateTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_updateTiles : function ( ) {
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( ! this . _map ) { return ; }
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var bounds = this . _map . getBounds ( ) ;
var zoom = this . _map . getZoom ( ) ;
var tileSize = this . tileSize ;
var mzoom = ( 1 << zoom ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var topLeft = new google . maps . LatLng (
bounds . getNorthEast ( ) . lat ( ) ,
bounds . getSouthWest ( ) . lng ( )
) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var bottomRigth = new google . maps . LatLng (
bounds . getSouthWest ( ) . lat ( ) ,
bounds . getNorthEast ( ) . lng ( )
) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . _projection = this . _map . getProjection ( ) ;
var divTopLeft = this . _projection . fromLatLngToPoint ( topLeft ) ;
var divBottomRight = this . _projection . fromLatLngToPoint ( bottomRigth ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var nwTilePoint = new google . maps . Point (
Math . floor ( divTopLeft . x * mzoom / tileSize ) ,
Math . floor ( divTopLeft . y * mzoom / tileSize ) ) ,
seTilePoint = new google . maps . Point (
Math . floor ( divBottomRight . x * mzoom / tileSize ) ,
Math . floor ( divBottomRight . y * mzoom / tileSize ) ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . _addTilesFromCenterOut ( nwTilePoint , seTilePoint ) ;
this . _removeOtherTiles ( nwTilePoint , seTilePoint ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
_removeOtherTiles : function ( nwTilePoint , seTilePoint ) {
var kArr , x , y , key ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var zoom = this . _map . getZoom ( ) ;
for ( key in this . _tiles ) {
if ( this . _tiles . hasOwnProperty ( key ) ) {
kArr = key . split ( ':' ) ;
x = parseInt ( kArr [ 0 ] , 10 ) ;
y = parseInt ( kArr [ 1 ] , 10 ) ;
z = parseInt ( kArr [ 2 ] , 10 ) ;
// remove tile if it's out of bounds
if ( z !== zoom || x < nwTilePoint . x || x > seTilePoint . x || y < nwTilePoint . y || y > seTilePoint . y ) {
this . _removeTile ( key ) ;
}
}
2014-02-24 21:36:39 +08:00
}
2014-12-10 01:05:11 +08:00
} ,
_removeTile : function ( key ) {
this . onTileRemoved && this . onTileRemoved ( this . _tiles [ key ] ) ;
delete this . _tiles [ key ] ;
delete this . _tilesLoading [ key ] ;
} ,
_tileKey : function ( tilePoint ) {
return tilePoint . x + ':' + tilePoint . y + ':' + tilePoint . zoom ;
} ,
_tileShouldBeLoaded : function ( tilePoint ) {
var k = this . _tileKey ( tilePoint ) ;
return ! ( k in this . _tiles ) && ! ( k in this . _tilesLoading ) ;
} ,
_tileLoaded : function ( tilePoint , tileData ) {
this . _tilesToLoad -- ;
var k = tilePoint . x + ':' + tilePoint . y + ':' + tilePoint . zoom
this . _tiles [ k ] = tileData ;
delete this . _tilesLoading [ k ] ;
if ( this . _tilesToLoad === 0 ) {
this . onTilesLoaded && this . onTilesLoaded ( ) ;
}
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
getTilePos : function ( tilePoint ) {
var limit = ( 1 << this . _map . getZoom ( ) ) ;
// wrap tile
tilePoint = {
x : ( ( tilePoint . x % limit ) + limit ) % limit ,
y : tilePoint . y
} ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
tilePoint = new google . maps . Point (
tilePoint . x * this . tileSize ,
tilePoint . y * this . tileSize
) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var bounds = this . _map . getBounds ( ) ;
var topLeft = new google . maps . LatLng (
bounds . getNorthEast ( ) . lat ( ) ,
bounds . getSouthWest ( ) . lng ( )
) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var divTopLeft = this . _map . getProjection ( ) . fromLatLngToPoint ( topLeft ) ;
zoom = ( 1 << this . _map . getZoom ( ) ) ;
divTopLeft . x = divTopLeft . x * zoom ;
divTopLeft . y = divTopLeft . y * zoom ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
return new google . maps . Point (
tilePoint . x - divTopLeft . x ,
tilePoint . y - divTopLeft . y
) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
_addTilesFromCenterOut : function ( nwTilePoint , seTilePoint ) {
var queue = [ ] ,
center = new google . maps . Point (
( nwTilePoint . x + seTilePoint . x ) * 0.5 ,
( nwTilePoint . y + seTilePoint . y ) * 0.5
) ,
zoom = this . _map . getZoom ( ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var j , i , point ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
for ( j = nwTilePoint . y ; j <= seTilePoint . y ; j ++ ) {
for ( i = nwTilePoint . x ; i <= seTilePoint . x ; i ++ ) {
point = new google . maps . Point ( i , j ) ;
point . zoom = zoom ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
if ( this . _tileShouldBeLoaded ( point ) ) {
queue . push ( point ) ;
2014-02-24 21:36:39 +08:00
}
}
2014-12-10 01:05:11 +08:00
}
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
var tilesToLoad = queue . length ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
if ( tilesToLoad === 0 ) { return ; }
function distanceToCenterSq ( point ) {
var dx = point . x - center . x ;
var dy = point . y - center . y ;
return dx * dx + dy * dy ;
2014-02-24 21:36:39 +08:00
}
2014-12-10 01:05:11 +08:00
// load tiles in order of their distance to center
queue . sort ( function ( a , b ) {
return distanceToCenterSq ( a ) - distanceToCenterSq ( b ) ;
} ) ;
this . _tilesToLoad += tilesToLoad ;
for ( i = 0 ; i < tilesToLoad ; i ++ ) {
var t = queue [ i ] ;
var k = this . _tileKey ( t ) ;
this . _tilesLoading [ k ] = t ;
// events
if ( this . onTileAdded ) {
this . onTileAdded ( t ) ;
2014-02-24 21:36:39 +08:00
}
}
2014-12-10 01:05:11 +08:00
this . onTilesLoading && this . onTilesLoading ( ) ;
}
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
}
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
module . exports = GMapsTileLoader ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
} , { } ] , 8 : [ function ( require , module , exports ) {
var gmaps = { } ;
if ( typeof google !== 'undefined' && typeof google . maps !== 'undefined' ) {
gmaps = require ( './torque' ) ;
gmaps . GMapsTileLoader = require ( './gmaps_tileloader_mixin' ) ;
}
module . exports = gmaps ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
} , { "./gmaps_tileloader_mixin" : 7 , "./torque" : 9 } ] , 9 : [ function ( require , module , exports ) {
( function ( global ) {
var carto = global . carto || require ( 'carto' ) ;
var torque = require ( '../' ) ;
var CanvasLayer = require ( './CanvasLayer' ) ;
var CanvasTileLayer = require ( './canvas_tile_layer' ) ;
var GMapsTileLoader = require ( './gmaps_tileloader_mixin' ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
function GMapsTorqueLayer ( options ) {
var self = this ;
if ( ! torque . isBrowserSupported ( ) ) {
throw new Error ( "browser is not supported by torque" ) ;
}
this . key = 0 ;
this . shader = null ;
this . ready = false ;
this . options = torque . extend ( { } , options ) ;
this . options = torque . extend ( {
provider : 'windshaft' ,
renderer : 'point' ,
resolution : 2 ,
steps : 100 ,
visible : true
} , this . options ) ;
if ( options . cartocss ) {
torque . extend ( this . options ,
torque . common . TorqueLayer . optionsFromCartoCSS ( options . cartocss ) ) ;
}
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . hidden = ! this . options . visible ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . animator = new torque . Animator ( function ( time ) {
var k = time | 0 ;
if ( self . key !== k ) {
self . setKey ( k ) ;
}
} , torque . clone ( this . options ) ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . play = this . animator . start . bind ( this . animator ) ;
this . stop = this . animator . stop . bind ( this . animator ) ;
this . pause = this . animator . pause . bind ( this . animator ) ;
this . toggle = this . animator . toggle . bind ( this . animator ) ;
this . setDuration = this . animator . duration . bind ( this . animator ) ;
this . isRunning = this . animator . isRunning . bind ( this . animator ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
CanvasLayer . call ( this , {
map : this . options . map ,
//resizeHandler: this.redraw,
animate : false ,
updateHandler : this . render ,
readyHandler : this . initialize
} ) ;
}
/ * *
* torque layer
* /
GMapsTorqueLayer . prototype = torque . extend ( { } ,
CanvasLayer . prototype ,
GMapsTileLoader . prototype ,
torque . Event ,
{
providers : {
'sql_api' : torque . providers . json ,
'url_template' : torque . providers . jsonarray ,
'windshaft' : torque . providers . windshaft
} ,
renderers : {
'point' : torque . renderer . Point ,
'pixel' : torque . renderer . Rectangle
} ,
initialize : function ( ) {
var self = this ;
this . onTileAdded = this . onTileAdded . bind ( this ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . options . ready = function ( ) {
self . fire ( "change:bounds" , {
bounds : self . provider . getBounds ( )
} ) ;
self . animator . steps ( self . provider . getSteps ( ) ) ;
self . animator . rescale ( ) ;
self . fire ( 'change:steps' , {
steps : self . provider . getSteps ( )
} ) ;
self . setKey ( self . key ) ;
} ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . provider = new this . providers [ this . options . provider ] ( this . options ) ;
this . renderer = new this . renderers [ this . options . renderer ] ( this . getCanvas ( ) , this . options ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
// this listener should be before tile loader
this . _cacheListener = google . maps . event . addListener ( this . options . map , 'zoom_changed' , function ( ) {
self . renderer && self . renderer . clearSpriteCache ( ) ;
} ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
this . _initTileLoader ( this . options . map , this . getProjection ( ) ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
if ( this . shader ) {
this . renderer . setShader ( this . shader ) ;
}
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
hide : function ( ) {
if ( this . hidden ) return this ;
this . pause ( ) ;
this . clear ( ) ;
this . hidden = true ;
return this ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
show : function ( ) {
if ( ! this . hidden ) return this ;
this . hidden = false ;
this . play ( ) ;
return this ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
setSQL : function ( sql ) {
if ( ! this . provider || ! this . provider . setSQL ) {
throw new Error ( "this provider does not support SQL" ) ;
}
this . provider . setSQL ( sql ) ;
this . _reloadTiles ( ) ;
return this ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
setBlendMode : function ( _ ) {
this . renderer && this . renderer . setBlendMode ( _ ) ;
this . redraw ( ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
setSteps : function ( steps ) {
this . provider && this . provider . setSteps ( steps ) ;
this . animator && this . animator . steps ( steps ) ;
this . _reloadTiles ( ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
setColumn : function ( column , isTime ) {
this . provider && this . provider . setColumn ( column , isTime ) ;
this . _reloadTiles ( ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
getTimeBounds : function ( ) {
return this . provider && this . provider . getKeySpan ( ) ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
getCanvas : function ( ) {
return this . canvas ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
// for each tile shown on the map request the data
onTileAdded : function ( t ) {
var self = this ;
this . provider . getTileData ( t , t . zoom , function ( tileData ) {
// don't load tiles that are not being shown
if ( t . zoom !== self . options . map . getZoom ( ) ) return ;
self . _tileLoaded ( t , tileData ) ;
if ( tileData ) {
self . redraw ( ) ;
2014-02-24 21:36:39 +08:00
}
2014-12-10 01:05:11 +08:00
} ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
clear : function ( ) {
var canvas = this . canvas ;
canvas . width = canvas . width ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* render the selectef key
* don 't call this function directly, it' s called by
* requestAnimationFrame . Use redraw to refresh it
* /
render : function ( ) {
if ( this . hidden ) return ;
var t , tile , pos ;
var canvas = this . canvas ;
2014-12-17 22:34:45 +08:00
this . renderer . clearCanvas ( ) ;
2014-12-10 01:05:11 +08:00
var ctx = canvas . getContext ( '2d' ) ;
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
// renders only a "frame"
for ( t in this . _tiles ) {
tile = this . _tiles [ t ] ;
if ( tile ) {
pos = this . getTilePos ( tile . coord ) ;
ctx . setTransform ( 1 , 0 , 0 , 1 , pos . x , pos . y ) ;
2015-03-04 18:08:46 +08:00
this . renderer . renderTile ( tile , this . key ) ;
2014-02-24 21:36:39 +08:00
}
2014-12-10 01:05:11 +08:00
}
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
getActivePointsBBox : function ( step ) {
var positions = [ ] ;
var tileMax = this . options . resolution * ( 256 / this . options . resolution - 1 ) ;
for ( var t in this . _tiles ) {
var tile = this . _tiles [ t ] ;
positions = positions . concat ( this . renderer . getActivePointsBBox ( tile , step ) ) ;
}
return positions ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* set key to be shown . If it ' s a single value
* it renders directly , if it ' s an array it renders
* accumulated
* /
setKey : function ( key ) {
this . key = key ;
this . animator . step ( key ) ;
this . redraw ( ) ;
this . fire ( 'change:time' , { time : this . getTime ( ) , step : this . key } ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* helper function , does the same than ` ` setKey ` ` but only
* accepts scalars .
* /
setStep : function ( time ) {
if ( time === undefined || time . length !== undefined ) {
throw new Error ( "setTime only accept scalars" ) ;
2014-02-24 21:36:39 +08:00
}
2014-12-10 01:05:11 +08:00
this . setKey ( time ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* transform from animation step to Date object
* that contains the animation time
*
* ` ` step ` ` should be between 0 and ` ` steps - 1 ` `
* /
stepToTime : function ( step ) {
if ( ! this . provider ) return 0 ;
var times = this . provider . getKeySpan ( ) ;
var time = times . start + ( times . end - times . start ) * ( step / this . provider . getSteps ( ) ) ;
return new Date ( time ) ;
} ,
2014-02-24 21:36:39 +08:00
2015-03-02 18:56:25 +08:00
timeToStep : function ( timestamp ) {
if ( typeof timestamp === "Date" ) timestamp = timestamp . getTime ( ) ;
if ( ! this . provider ) return 0 ;
var times = this . provider . getKeySpan ( ) ;
var step = ( this . provider . getSteps ( ) * ( timestamp - times . start ) ) / ( times . end - times . start ) ;
return step ;
} ,
2014-12-10 01:05:11 +08:00
getStep : function ( ) {
return this . key ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* returns the animation time defined by the data
* in the defined column . Date object
* /
getTime : function ( ) {
return this . stepToTime ( this . key ) ;
} ,
2014-02-24 21:36:39 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* set the cartocss for the current renderer
* /
setCartoCSS : function ( cartocss ) {
var shader = new carto . RendererJS ( ) . render ( cartocss ) ;
this . shader = shader ;
if ( this . renderer ) {
this . renderer . setShader ( shader ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// provider options
var options = torque . common . TorqueLayer . optionsFromLayer ( shader . findLayer ( { name : 'Map' } ) ) ;
this . provider && this . provider . setCartoCSS && this . provider . setCartoCSS ( cartocss ) ;
if ( this . provider && this . provider . setOptions ( options ) ) {
this . _reloadTiles ( ) ;
}
torque . extend ( this . options , options ) ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
// animator options
if ( options . animationDuration ) {
this . animator . duration ( options . animationDuration ) ;
}
2013-11-11 23:46:42 +08:00
2014-12-10 01:05:11 +08:00
this . redraw ( ) ;
return this ;
} ,
2013-11-11 23:46:42 +08:00
2014-12-10 01:05:11 +08:00
redraw : function ( ) {
this . scheduleUpdate ( ) ;
} ,
2014-10-21 23:16:23 +08:00
2014-12-10 01:05:11 +08:00
onRemove : function ( ) {
CanvasLayer . prototype . onRemove . call ( this ) ;
this . animator . stop ( ) ;
this . _removeTileLoader ( ) ;
google . maps . event . removeListener ( this . _cacheListener ) ;
}
2013-11-11 23:46:42 +08:00
2014-12-10 01:05:11 +08:00
} ) ;
2013-11-11 23:46:42 +08:00
2014-12-10 01:05:11 +08:00
function GMapsTiledTorqueLayer ( options ) {
this . options = torque . extend ( { } , options ) ;
CanvasTileLayer . call ( this , this . _loadTile . bind ( this ) , this . drawTile . bind ( this ) ) ;
this . initialize ( options ) ;
}
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
GMapsTiledTorqueLayer . prototype = torque . extend ( { } , CanvasTileLayer . prototype , {
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
providers : {
'sql_api' : torque . providers . json ,
'url_template' : torque . providers . JsonArray
} ,
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
renderers : {
'point' : torque . renderer . Point ,
'pixel' : torque . renderer . Rectangle
} ,
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
initialize : function ( options ) {
var self = this ;
this . key = 0 ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . options . renderer = this . options . renderer || 'pixel' ;
this . options . provider = this . options . provider || 'sql_api' ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
this . provider = new this . providers [ this . options . provider ] ( options ) ;
this . renderer = new this . renderers [ this . options . renderer ] ( null , options ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} ,
_tileLoaded : function ( tile , tileData ) {
tile . data = tileData ;
this . drawTile ( tile ) ;
} ,
_loadTile : function ( tile , coord , zoom ) {
var self = this ;
var limit = 1 << zoom ;
// wrap tile
var wrappedCoord = {
x : ( ( coord . x % limit ) + limit ) % limit ,
y : coord . y
} ;
this . provider . getTileData ( wrappedCoord , zoom , function ( tileData ) {
self . _tileLoaded ( tile , tileData ) ;
2013-11-09 00:09:23 +08:00
} ) ;
2014-12-10 01:05:11 +08:00
} ,
2013-11-09 00:09:23 +08:00
2014-12-10 01:05:11 +08:00
drawTile : function ( tile ) {
var canvas = tile . canvas ;
if ( ! tile . data ) return ;
canvas . width = canvas . width ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . renderer . setCanvas ( canvas ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var accum = this . renderer . accumulate ( tile . data , this . key ) ;
this . renderer . renderTileAccum ( accum , 0 , 0 ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setKey : function ( key ) {
this . key = key ;
this . redraw ( ) ;
} ,
2014-10-21 23:16:23 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* set the cartocss for the current renderer
* /
setCartoCSS : function ( cartocss ) {
if ( ! this . renderer ) throw new Error ( 'renderer is not valid' ) ;
return this . renderer . setCartoCSS ( cartocss ) ;
}
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
} ) ;
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
module . exports = {
GMapsTiledTorqueLayer : GMapsTiledTorqueLayer ,
GMapsTorqueLayer : GMapsTorqueLayer
} ;
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { "../" : 10 , "./CanvasLayer" : 5 , "./canvas_tile_layer" : 6 , "./gmaps_tileloader_mixin" : 7 , "carto" : undefined } ] , 10 : [ function ( require , module , exports ) {
module . exports = require ( './core' ) ;
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
module . exports . Animator = require ( './animator' ) ;
module . exports . cartocss _reference = require ( './cartocss_reference' ) ;
module . exports . common = require ( './common' ) ;
module . exports . math = require ( './math' ) ;
module . exports . Mercator = require ( './mercator' ) ;
module . exports . net = require ( './request' ) ;
module . exports . renderer = require ( './renderer' ) ;
module . exports . providers = require ( './provider' ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
require ( './leaflet' ) ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
var gmaps = require ( './gmaps' ) ;
module . exports . GMapsTileLoader = gmaps . GMapsTileLoader ;
2014-12-10 01:23:38 +08:00
module . exports . GMapsTorqueLayer = gmaps . GMapsTorqueLayer ;
2014-12-10 01:05:11 +08:00
module . exports . GMapsTiledTorqueLayer = gmaps . GMapsTiledTorqueLayer ;
2013-11-23 01:31:04 +08:00
2015-03-02 18:56:25 +08:00
} , { "./animator" : 1 , "./cartocss_reference" : 2 , "./common" : 3 , "./core" : 4 , "./gmaps" : 8 , "./leaflet" : 12 , "./math" : 15 , "./mercator" : 16 , "./provider" : 18 , "./renderer" : 23 , "./request" : 27 } ] , 11 : [ function ( require , module , exports ) {
2014-12-10 01:05:11 +08:00
require ( './leaflet_tileloader_mixin' ) ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* full canvas layer implementation for Leaflet
* /
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
L . CanvasLayer = L . Class . extend ( {
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
includes : [ L . Mixin . Events , L . Mixin . TileLoader ] ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
options : {
minZoom : 0 ,
maxZoom : 28 ,
tileSize : 256 ,
subdomains : 'abc' ,
errorTileUrl : '' ,
attribution : '' ,
zoomOffset : 0 ,
opacity : 1 ,
unloadInvisibleTiles : L . Browser . mobile ,
updateWhenIdle : L . Browser . mobile ,
tileLoader : false , // installs tile loading events
zoomAnimation : true
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
initialize : function ( options ) {
var self = this ;
options = options || { } ;
//this.project = this._project.bind(this);
this . render = this . render . bind ( this ) ;
L . Util . setOptions ( this , options ) ;
this . _canvas = this . _createCanvas ( ) ;
// backCanvas for zoom animation
if ( this . options . zoomAnimation ) {
this . _backCanvas = this . _createCanvas ( ) ;
}
this . _ctx = this . _canvas . getContext ( '2d' ) ;
this . currentAnimationFrame = - 1 ;
this . requestAnimationFrame = window . requestAnimationFrame || window . mozRequestAnimationFrame ||
window . webkitRequestAnimationFrame || window . msRequestAnimationFrame || function ( callback ) {
return window . setTimeout ( callback , 1000 / 60 ) ;
} ;
this . cancelAnimationFrame = window . cancelAnimationFrame || window . mozCancelAnimationFrame ||
window . webkitCancelAnimationFrame || window . msCancelAnimationFrame || function ( id ) { clearTimeout ( id ) ; } ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_createCanvas : function ( ) {
var canvas ;
canvas = document . createElement ( 'canvas' ) ;
canvas . style . position = 'absolute' ;
canvas . style . top = 0 ;
canvas . style . left = 0 ;
canvas . style . pointerEvents = "none" ;
canvas . style . zIndex = this . options . zIndex || 0 ;
var className = 'leaflet-tile-container' ;
if ( this . options . zoomAnimation ) {
className += ' leaflet-zoom-animated' ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
canvas . setAttribute ( 'class' , className ) ;
return canvas ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
onAdd : function ( map ) {
this . _map = map ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// add container with the canvas to the tile pane
// the container is moved in the oposite direction of the
// map pane to keep the canvas always in (0, 0)
var tilePane = this . _map . _panes . tilePane ;
var _container = L . DomUtil . create ( 'div' , 'leaflet-layer' ) ;
_container . appendChild ( this . _canvas ) ;
if ( this . options . zoomAnimation ) {
_container . appendChild ( this . _backCanvas ) ;
this . _backCanvas . style . display = 'none' ;
}
tilePane . appendChild ( _container ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . _container = _container ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// hack: listen to predrag event launched by dragging to
// set container in position (0, 0) in screen coordinates
map . dragging . _draggable . on ( 'predrag' , function ( ) {
var d = map . dragging . _draggable ;
L . DomUtil . setPosition ( this . _canvas , { x : - d . _newPos . x , y : - d . _newPos . y } ) ;
} , this ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
map . on ( { 'viewreset' : this . _reset } , this ) ;
map . on ( 'move' , this . render , this ) ;
map . on ( 'resize' , this . _reset , this ) ;
2014-01-09 23:56:06 +08:00
2014-12-10 01:05:11 +08:00
if ( this . options . zoomAnimation ) {
map . on ( {
'zoomanim' : this . _animateZoom ,
'zoomend' : this . _endZoomAnim ,
'moveend' : this . _reset
} , this ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( this . options . tileLoader ) {
this . _initTileLoader ( ) ;
}
2014-10-21 23:16:23 +08:00
2014-12-10 01:05:11 +08:00
this . _reset ( ) ;
} ,
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
_animateZoom : function ( e ) {
if ( ! this . _animating ) {
this . _animating = true ;
}
var back = this . _backCanvas ;
2014-07-07 20:51:01 +08:00
2014-12-10 01:05:11 +08:00
back . width = this . _canvas . width ;
back . height = this . _canvas . height ;
2014-07-07 20:51:01 +08:00
2014-12-10 01:05:11 +08:00
// paint current canvas in back canvas with trasnformation
var pos = this . _canvas . _leaflet _pos || { x : 0 , y : 0 } ;
back . getContext ( '2d' ) . drawImage ( this . _canvas , 0 , 0 ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
L . DomUtil . setPosition ( back , L . DomUtil . getPosition ( this . _canvas ) ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// hide original
this . _canvas . style . display = 'none' ;
back . style . display = 'block' ;
var map = this . _map ;
var scale = map . getZoomScale ( e . zoom ) ;
var newCenter = map . _latLngToNewLayerPoint ( map . getCenter ( ) , e . zoom , e . center ) ;
var oldCenter = map . _latLngToNewLayerPoint ( e . center , e . zoom , e . center ) ;
var origin = {
x : newCenter . x - oldCenter . x + pos . x ,
y : newCenter . y - oldCenter . y + pos . y ,
} ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
var bg = back ;
var transform = L . DomUtil . TRANSFORM ;
setTimeout ( function ( ) {
bg . style [ transform ] = L . DomUtil . getTranslateString ( origin ) + ' scale(' + e . scale + ') ' ;
} , 0 )
} ,
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
_endZoomAnim : function ( ) {
this . _animating = false ;
this . _canvas . style . display = 'block' ;
this . _backCanvas . style . display = 'none' ;
this . _backCanvas . style [ L . DomUtil . TRANSFORM ] = '' ;
} ,
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
getCanvas : function ( ) {
return this . _canvas ;
} ,
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
getAttribution : function ( ) {
return this . options . attribution ;
} ,
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
draw : function ( ) {
return this . _reset ( ) ;
} ,
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
onRemove : function ( map ) {
this . _container . parentNode . removeChild ( this . _container ) ;
map . off ( {
'viewreset' : this . _reset ,
'move' : this . _render ,
'moveend' : this . _reset ,
'resize' : this . _reset ,
'zoomanim' : this . _animateZoom ,
'zoomend' : this . _endZoomAnim
} , this ) ;
} ,
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
addTo : function ( map ) {
map . addLayer ( this ) ;
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setOpacity : function ( opacity ) {
this . options . opacity = opacity ;
this . _updateOpacity ( ) ;
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setZIndex : function ( zIndex ) {
this . _canvas . style . zIndex = zIndex ;
if ( this . options . zoomAnimation ) {
this . _backCanvas . style . zIndex = zIndex ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
bringToFront : function ( ) {
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
bringToBack : function ( ) {
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_reset : function ( ) {
var size = this . _map . getSize ( ) ;
this . _canvas . width = size . x ;
this . _canvas . height = size . y ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// fix position
var pos = L . DomUtil . getPosition ( this . _map . getPanes ( ) . mapPane ) ;
if ( pos ) {
L . DomUtil . setPosition ( this . _canvas , { x : - pos . x , y : - pos . y } ) ;
}
this . onResize ( ) ;
this . _render ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ *
_project : function ( x ) {
var point = this . _map . latLngToLayerPoint ( new L . LatLng ( x [ 1 ] , x [ 0 ] ) ) ;
return [ point . x , point . y ] ;
} ,
* /
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_updateOpacity : function ( ) { } ,
2013-08-01 18:04:39 +08:00
2014-12-10 01:05:11 +08:00
_render : function ( ) {
if ( this . currentAnimationFrame >= 0 ) {
this . cancelAnimationFrame . call ( window , this . currentAnimationFrame ) ;
}
this . currentAnimationFrame = this . requestAnimationFrame . call ( window , this . render ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// use direct: true if you are inside an animation frame call
redraw : function ( direct ) {
if ( direct ) {
this . render ( ) ;
} else {
this . _render ( ) ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
onResize : function ( ) {
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
render : function ( ) {
throw new Error ( 'render function should be implemented' ) ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} ) ;
} , { "./leaflet_tileloader_mixin" : 13 } ] , 12 : [ function ( require , module , exports ) {
if ( typeof L !== 'undefined' ) {
require ( './torque' ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} , { "./torque" : 14 } ] , 13 : [ function ( require , module , exports ) {
L . Mixin . TileLoader = {
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_initTileLoader : function ( ) {
this . _tiles = { }
this . _tilesLoading = { } ;
this . _tilesToLoad = 0 ;
this . _map . on ( {
'moveend' : this . _updateTiles
} , this ) ;
this . _updateTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_removeTileLoader : function ( ) {
this . _map . off ( {
'moveend' : this . _updateTiles
} , this ) ;
this . _removeTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_updateTiles : function ( ) {
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( ! this . _map ) { return ; }
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var bounds = this . _map . getPixelBounds ( ) ,
zoom = this . _map . getZoom ( ) ,
tileSize = this . options . tileSize ;
if ( zoom > this . options . maxZoom || zoom < this . options . minZoom ) {
return ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
var nwTilePoint = new L . Point (
Math . floor ( bounds . min . x / tileSize ) ,
Math . floor ( bounds . min . y / tileSize ) ) ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
seTilePoint = new L . Point (
Math . floor ( bounds . max . x / tileSize ) ,
Math . floor ( bounds . max . y / tileSize ) ) ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
tileBounds = new L . Bounds ( nwTilePoint , seTilePoint ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . _addTilesFromCenterOut ( tileBounds ) ;
this . _removeOtherTiles ( tileBounds ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_removeTiles : function ( bounds ) {
for ( var key in this . _tiles ) {
this . _removeTile ( key ) ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_reloadTiles : function ( ) {
this . _removeTiles ( ) ;
this . _updateTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_removeOtherTiles : function ( bounds ) {
var kArr , x , y , z , key ;
var zoom = this . _map . getZoom ( ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
for ( key in this . _tiles ) {
if ( this . _tiles . hasOwnProperty ( key ) ) {
kArr = key . split ( ':' ) ;
x = parseInt ( kArr [ 0 ] , 10 ) ;
y = parseInt ( kArr [ 1 ] , 10 ) ;
z = parseInt ( kArr [ 2 ] , 10 ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// remove tile if it's out of bounds
if ( zoom !== z || x < bounds . min . x || x > bounds . max . x || y < bounds . min . y || y > bounds . max . y ) {
this . _removeTile ( key ) ;
}
}
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_removeTile : function ( key ) {
this . fire ( 'tileRemoved' , this . _tiles [ key ] ) ;
delete this . _tiles [ key ] ;
delete this . _tilesLoading [ key ] ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_tileKey : function ( tilePoint ) {
return tilePoint . x + ':' + tilePoint . y + ':' + tilePoint . zoom ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_tileShouldBeLoaded : function ( tilePoint ) {
var k = this . _tileKey ( tilePoint ) ;
return ! ( k in this . _tiles ) && ! ( k in this . _tilesLoading ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_tileLoaded : function ( tilePoint , tileData ) {
this . _tilesToLoad -- ;
var k = tilePoint . x + ':' + tilePoint . y + ':' + tilePoint . zoom
this . _tiles [ k ] = tileData ;
delete this . _tilesLoading [ k ] ;
if ( this . _tilesToLoad === 0 ) {
this . fire ( "tilesLoaded" ) ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
getTilePos : function ( tilePoint ) {
tilePoint = new L . Point ( tilePoint . x , tilePoint . y ) ;
var origin = this . _map . _getNewTopLeftPoint ( this . _map . getCenter ( ) ) ,
tileSize = this . options . tileSize ;
return tilePoint . multiplyBy ( tileSize ) . subtract ( origin ) ;
} ,
_addTilesFromCenterOut : function ( bounds ) {
var queue = [ ] ,
center = bounds . getCenter ( ) ,
zoom = this . _map . getZoom ( ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var j , i , point ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
for ( j = bounds . min . y ; j <= bounds . max . y ; j ++ ) {
for ( i = bounds . min . x ; i <= bounds . max . x ; i ++ ) {
point = new L . Point ( i , j ) ;
point . zoom = zoom ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( this . _tileShouldBeLoaded ( point ) ) {
queue . push ( point ) ;
}
}
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var tilesToLoad = queue . length ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( tilesToLoad === 0 ) { return ; }
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// load tiles in order of their distance to center
queue . sort ( function ( a , b ) {
return a . distanceTo ( center ) - b . distanceTo ( center ) ;
} ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . _tilesToLoad += tilesToLoad ;
for ( i = 0 ; i < tilesToLoad ; i ++ ) {
var t = queue [ i ] ;
var k = this . _tileKey ( t ) ;
this . _tilesLoading [ k ] = t ;
this . fire ( 'tileAdded' , t ) ;
}
this . fire ( "tilesLoading" ) ;
2013-07-30 16:45:35 +08:00
}
2015-01-14 23:51:32 +08:00
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
} , { } ] , 14 : [ function ( require , module , exports ) {
( function ( global ) {
var carto = global . carto || require ( 'carto' ) ;
var torque = require ( '../' ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
require ( './canvas_layer' ) ;
2013-07-30 16:45:35 +08:00
/ * *
2014-12-10 01:05:11 +08:00
* torque layer
2013-07-30 16:45:35 +08:00
* /
2014-12-10 01:05:11 +08:00
L . TorqueLayer = L . CanvasLayer . extend ( {
providers : {
'sql_api' : torque . providers . json ,
'url_template' : torque . providers . jsonarray ,
'windshaft' : torque . providers . windshaft
} ,
renderers : {
'point' : torque . renderer . Point ,
'pixel' : torque . renderer . Rectangle
} ,
initialize : function ( options ) {
var self = this ;
if ( ! torque . isBrowserSupported ( ) ) {
throw new Error ( "browser is not supported by torque" ) ;
}
options . tileLoader = true ;
this . key = 0 ;
this . prevRenderedKey = 0 ;
if ( options . cartocss ) {
torque . extend ( options , torque . common . TorqueLayer . optionsFromCartoCSS ( options . cartocss ) ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
options . resolution = options . resolution || 2 ;
options . steps = options . steps || 100 ;
options . visible = options . visible === undefined ? true : options . visible ;
this . hidden = ! options . visible ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . animator = new torque . Animator ( function ( time ) {
var k = time | 0 ;
if ( self . key !== k ) {
self . setKey ( k , { direct : true } ) ;
}
} , torque . extend ( torque . clone ( options ) , {
onPause : function ( ) {
self . fire ( 'pause' ) ;
} ,
onStop : function ( ) {
self . fire ( 'stop' ) ;
} ,
onStart : function ( ) {
self . fire ( 'play' ) ;
}
} ) ) ;
this . play = this . animator . start . bind ( this . animator ) ;
this . stop = this . animator . stop . bind ( this . animator ) ;
this . pause = this . animator . pause . bind ( this . animator ) ;
this . toggle = this . animator . toggle . bind ( this . animator ) ;
this . setDuration = this . animator . duration . bind ( this . animator ) ;
this . isRunning = this . animator . isRunning . bind ( this . animator ) ;
L . CanvasLayer . prototype . initialize . call ( this , options ) ;
this . options . renderer = this . options . renderer || 'point' ;
this . options . provider = this . options . provider || 'windshaft' ;
options . ready = function ( ) {
self . fire ( "change:bounds" , {
bounds : self . provider . getBounds ( )
} ) ;
self . animator . steps ( self . provider . getSteps ( ) ) ;
self . animator . rescale ( ) ;
self . fire ( 'change:steps' , {
steps : self . provider . getSteps ( )
} ) ;
self . setKey ( self . key ) ;
2013-07-30 16:45:35 +08:00
} ;
2014-12-10 01:05:11 +08:00
this . provider = new this . providers [ this . options . provider ] ( options ) ;
this . renderer = new this . renderers [ this . options . renderer ] ( this . getCanvas ( ) , options ) ;
2013-07-30 16:45:35 +08:00
2015-03-02 18:56:25 +08:00
this . renderer . on ( "allIconsLoaded" , this . render . bind ( this ) ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// for each tile shown on the map request the data
this . on ( 'tileAdded' , function ( t ) {
var tileData = this . provider . getTileData ( t , t . zoom , function ( tileData ) {
// don't load tiles that are not being shown
if ( t . zoom !== self . _map . getZoom ( ) ) return ;
self . _tileLoaded ( t , tileData ) ;
self . _clearTileCaches ( ) ;
if ( tileData ) {
self . redraw ( ) ;
}
} ) ;
} , this ) ;
2013-07-30 16:45:35 +08:00
2015-01-14 23:51:32 +08:00
} ,
2014-12-10 01:05:11 +08:00
_clearTileCaches : function ( ) {
var t , tile ;
for ( t in this . _tiles ) {
tile = this . _tiles [ t ] ;
if ( tile && tile . _tileCache ) {
tile . _tileCache = null ;
}
}
} ,
_clearCaches : function ( ) {
this . renderer && this . renderer . clearSpriteCache ( ) ;
this . _clearTileCaches ( ) ;
} ,
onAdd : function ( map ) {
map . on ( {
'zoomend' : this . _clearCaches ,
'zoomstart' : this . _pauseOnZoom ,
} , this ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
map . on ( {
'zoomend' : this . _resumeOnZoom
} , this ) ;
L . CanvasLayer . prototype . onAdd . call ( this , map ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
onRemove : function ( map ) {
this . _removeTileLoader ( ) ;
map . off ( {
'zoomend' : this . _clearCaches ,
'zoomstart' : this . _pauseOnZoom ,
} , this ) ;
map . off ( {
'zoomend' : this . _resumeOnZoom
} , this ) ;
L . CanvasLayer . prototype . onRemove . call ( this , map ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_pauseOnZoom : function ( ) {
this . wasRunning = this . isRunning ( ) ;
if ( this . wasRunning ) {
this . pause ( ) ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
_resumeOnZoom : function ( ) {
if ( this . wasRunning ) {
this . play ( ) ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
hide : function ( ) {
if ( this . hidden ) return this ;
this . pause ( ) ;
this . clear ( ) ;
this . hidden = true ;
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
show : function ( ) {
if ( ! this . hidden ) return this ;
this . hidden = false ;
this . play ( ) ;
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setSQL : function ( sql ) {
if ( ! this . provider || ! this . provider . setSQL ) {
throw new Error ( "this provider does not support SQL" ) ;
}
this . provider . setSQL ( sql ) ;
this . _reloadTiles ( ) ;
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setBlendMode : function ( _ ) {
this . renderer . setBlendMode ( _ ) ;
this . redraw ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setSteps : function ( steps ) {
this . provider . setSteps ( steps ) ;
this . _reloadTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setColumn : function ( column , isTime ) {
this . provider . setColumn ( column , isTime ) ;
this . _reloadTiles ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
getTimeBounds : function ( ) {
return this . provider && this . provider . getKeySpan ( ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
clear : function ( ) {
var canvas = this . getCanvas ( ) ;
canvas . width = canvas . width ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* render the selectef key
* don 't call this function directly, it' s called by
* requestAnimationFrame . Use redraw to refresh it
* /
render : function ( ) {
if ( this . hidden ) return ;
var t , tile , pos ;
var canvas = this . getCanvas ( ) ;
2014-12-17 22:34:45 +08:00
this . renderer . clearCanvas ( ) ;
2014-12-10 01:05:11 +08:00
var ctx = canvas . getContext ( '2d' ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
for ( t in this . _tiles ) {
tile = this . _tiles [ t ] ;
if ( tile ) {
// clear cache
if ( this . animator . isRunning ( ) ) {
tile . _tileCache = null ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
pos = this . getTilePos ( tile . coord ) ;
ctx . setTransform ( 1 , 0 , 0 , 1 , pos . x , pos . y ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( tile . _tileCache ) {
// when the tile has a cached image just render it and avoid to render
// all the points
this . renderer . _ctx . drawImage ( tile . _tileCache , 0 , 0 ) ;
} else {
this . renderer . renderTile ( tile , this . key ) ;
}
}
}
2015-03-02 18:56:25 +08:00
this . renderer . applyFilters ( ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// prepare caches if the animation is not running
// don't cache if the key has just changed, this avoids to cache
// when the user is dragging, it only cache when the map is still
if ( ! this . animator . isRunning ( ) && this . key === this . prevRenderedKey ) {
var tile _size = this . renderer . TILE _SIZE ;
for ( t in this . _tiles ) {
tile = this . _tiles [ t ] ;
if ( tile && ! tile . _tileCache ) {
var c = tile . _tileCache = document . createElement ( 'canvas' ) ;
c . width = c . height = tile _size ;
pos = this . getTilePos ( tile . coord ) ;
2014-12-17 22:34:45 +08:00
// clip bounds, firefox raise an exception when try to get data from outside canvas
var x = Math . max ( 0 , pos . x )
var y = Math . max ( 0 , pos . y )
var w = Math . min ( tile _size , this . getCanvas ( ) . width - x ) ;
var h = Math . min ( tile _size , this . getCanvas ( ) . height - y ) ;
if ( w > 0 && h > 0 ) {
c . getContext ( '2d' ) . drawImage ( this . getCanvas ( ) , x , y , w , h , x - pos . x , y - pos . y , w , h ) ;
}
2014-12-10 01:05:11 +08:00
}
}
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
this . prevRenderedKey = this . key ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* set key to be shown . If it ' s a single value
* it renders directly , if it ' s an array it renders
* accumulated
* /
setKey : function ( key , options ) {
this . key = key ;
this . animator . step ( key ) ;
this . _clearTileCaches ( ) ;
this . redraw ( options && options . direct ) ;
this . fire ( 'change:time' , { time : this . getTime ( ) , step : this . key } ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* helper function , does the same than ` ` setKey ` ` but only
* accepts scalars .
* /
setStep : function ( time ) {
if ( time === undefined || time . length !== undefined ) {
throw new Error ( "setTime only accept scalars" ) ;
}
this . setKey ( time ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* transform from animation step to Date object
* that contains the animation time
*
* ` ` step ` ` should be between 0 and ` ` steps - 1 ` `
* /
stepToTime : function ( step ) {
var times = this . provider . getKeySpan ( ) ;
var time = times . start + ( times . end - times . start ) * ( step / this . provider . getSteps ( ) ) ;
return new Date ( time ) ;
} ,
2015-03-02 18:56:25 +08:00
timeToStep : function ( timestamp ) {
if ( typeof timestamp === "Date" ) timestamp = timestamp . getTime ( ) ;
if ( ! this . provider ) return 0 ;
var times = this . provider . getKeySpan ( ) ;
var step = ( this . provider . getSteps ( ) * ( timestamp - times . start ) ) / ( times . end - times . start ) ;
return step ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
getStep : function ( ) {
return this . key ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* returns the animation time defined by the data
* in the defined column . Date object
* /
getTime : function ( ) {
return this . stepToTime ( this . key ) ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* returns an object with the start and end times
* /
getTimeSpan : function ( ) {
2015-03-02 18:56:25 +08:00
return this . provider . getKeySpan ( ) ;
2014-12-10 01:05:11 +08:00
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* set the cartocss for the current renderer
* /
setCartoCSS : function ( cartocss ) {
if ( ! this . renderer ) throw new Error ( 'renderer is not valid' ) ;
var shader = new carto . RendererJS ( ) . render ( cartocss ) ;
this . renderer . setShader ( shader ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// provider options
var options = torque . common . TorqueLayer . optionsFromLayer ( shader . findLayer ( { name : 'Map' } ) ) ;
this . provider . setCartoCSS && this . provider . setCartoCSS ( cartocss ) ;
if ( this . provider . setOptions ( options ) ) {
this . _reloadTiles ( ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
torque . extend ( this . options , options ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// animator options
if ( options . animationDuration ) {
this . animator . duration ( options . animationDuration ) ;
}
2015-03-02 18:56:25 +08:00
this . _clearCaches ( ) ;
2014-12-10 01:05:11 +08:00
this . redraw ( ) ;
return this ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* get active points for a step in active zoom
* returns a list of bounding boxes [ [ ] , [ ] , [ ] ]
* empty list if there is no active pixels
* /
getActivePointsBBox : function ( step ) {
var positions = [ ] ;
for ( var t in this . _tiles ) {
var tile = this . _tiles [ t ] ;
positions = positions . concat ( this . renderer . getActivePointsBBox ( tile , step ) ) ;
}
return positions ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* return the value for position relative to map coordinates . null for no value
* /
getValueForPos : function ( x , y , step ) {
step = step === undefined ? this . key : step ;
var t , tile , pos , value = null , xx , yy ;
for ( t in this . _tiles ) {
tile = this . _tiles [ t ] ;
pos = this . getTilePos ( tile . coord ) ;
xx = x - pos . x ;
yy = y - pos . y ;
if ( xx >= 0 && yy >= 0 && xx < this . renderer . TILE _SIZE && yy <= this . renderer . TILE _SIZE ) {
value = this . renderer . getValueFor ( tile , step , xx , yy ) ;
}
if ( value !== null ) {
return value ;
}
}
return null ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
invalidate : function ( ) {
this . provider . reload ( ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
} ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { "../" : 10 , "./canvas_layer" : 11 , "carto" : undefined } ] , 15 : [ function ( require , module , exports ) {
function clamp ( a , b ) {
return function ( t ) {
return Math . max ( Math . min ( t , b ) , a ) ;
} ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
function invLinear ( a , b ) {
var c = clamp ( 0 , 1.0 ) ;
return function ( t ) {
return c ( ( t - a ) / ( b - a ) ) ;
} ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
function linear ( a , b ) {
var c = clamp ( a , b ) ;
function _linear ( t ) {
return c ( a * ( 1.0 - t ) + t * b ) ;
}
_linear . invert = function ( ) {
return invLinear ( a , b ) ;
} ;
return _linear ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
module . exports = {
clamp : clamp ,
linear : linear ,
invLinear : invLinear
2013-07-30 16:45:35 +08:00
} ;
2014-12-10 01:05:11 +08:00
} , { } ] , 16 : [ function ( require , module , exports ) {
var Point = function ( x , y ) {
this . x = x || 0 ;
this . y = y || 0 ;
2013-07-30 16:45:35 +08:00
} ;
2014-12-10 01:05:11 +08:00
function clamp ( value , optMin , optMax ) {
if ( optMin !== null ) value = Math . max ( value , optMin ) ;
if ( optMax !== null ) value = Math . min ( value , optMax ) ;
return value ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
function degreesToRadians ( deg ) {
return deg * ( Math . PI / 180 ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
function radiansToDegrees ( rad ) {
return rad / ( Math . PI / 180 ) ;
2013-08-29 21:22:57 +08:00
}
2013-07-30 16:45:35 +08:00
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var MercatorProjection = function ( ) {
// this._tileSize = L.Browser.retina ? 512 : 256;
this . _tileSize = 256 ;
this . _pixelOrigin = new Point ( this . _tileSize / 2 , this . _tileSize / 2 ) ;
this . _pixelsPerLonDegree = this . _tileSize / 360 ;
this . _pixelsPerLonRadian = this . _tileSize / ( 2 * Math . PI ) ;
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
MercatorProjection . prototype . _fromLatLonToPoint = function ( lat , lon ) {
var point = new Point ( 0 , 0 ) ;
var origin = this . _pixelOrigin ;
point . x = origin . x + lon * this . _pixelsPerLonDegree ;
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
// 89.189. This is about a third of a tile past the edge of the world
// tile.
var siny = clamp ( Math . sin ( degreesToRadians ( lat ) ) , - 0.9999 , 0.9999 ) ;
point . y = origin . y + 0.5 * Math . log ( ( 1 + siny ) / ( 1 - siny ) ) * - this . _pixelsPerLonRadian ;
return point ;
} ;
MercatorProjection . prototype . _fromPointToLatLon = function ( point ) {
var me = this ;
var origin = me . _pixelOrigin ;
var lon = ( point . x - origin . x ) / me . _pixelsPerLonDegree ;
var latRadians = ( point . y - origin . y ) / - me . _pixelsPerLonRadian ;
var lat = radiansToDegrees ( 2 * Math . atan ( Math . exp ( latRadians ) ) - Math . PI / 2 ) ;
return { lat : lat , lon : lon } ;
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
MercatorProjection . prototype . _tilePixelPos = function ( tileX , tileY ) {
return {
x : tileX * this . _tileSize ,
y : tileY * this . _tileSize
} ;
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
MercatorProjection . prototype . tilePixelBBox = function ( x , y , zoom , px , py , res ) {
res = res || 1.0 ;
var numTiles = 1 << zoom ;
var inc = res / numTiles ;
px = ( x * this . _tileSize + px ) / numTiles ;
py = ( y * this . _tileSize + py ) / numTiles ;
return [
this . _fromPointToLatLon ( new Point ( px , py + inc ) ) ,
this . _fromPointToLatLon ( new Point ( px + inc , py ) )
] ;
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
MercatorProjection . prototype . tileBBox = function ( x , y , zoom , bufferSize ) {
var numTiles = 1 << zoom ;
bufferSize = bufferSize || 0 ;
var inc = ( this . _tileSize + bufferSize * 2 ) / numTiles ;
var px = ( x * this . _tileSize - bufferSize ) / numTiles ;
var py = ( y * this . _tileSize - bufferSize ) / numTiles ;
return [
this . _fromPointToLatLon ( new Point ( px , py + inc ) ) ,
this . _fromPointToLatLon ( new Point ( px + inc , py ) )
] ;
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
MercatorProjection . prototype . latLonToTilePoint = function ( lat , lon , tileX , tileY , zoom ) {
var numTiles = 1 << zoom ;
var worldCoordinate = this . _fromLatLonToPoint ( lat , lon ) ;
var pixelCoordinate = new Point ( worldCoordinate . x * numTiles , worldCoordinate . y * numTiles ) ;
var tilePixelPos = this . _tilePixelPos ( tileX , tileY ) ;
return new Point ( Math . round ( pixelCoordinate . x - tilePixelPos . x ) , Math . round ( pixelCoordinate . y - tilePixelPos . y ) ) ;
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
module . exports = MercatorProjection ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} , { } ] , 17 : [ function ( require , module , exports ) {
/ *
# metrics profiler
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
# # timing
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
` ` `
var timer = Profiler . metric ( 'resource:load' )
time . start ( ) ;
...
time . end ( ) ;
` ` `
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
# # counters
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
` ` `
var counter = Profiler . metric ( 'requests' )
counter . inc ( ) ; // 1
counter . inc ( 10 ) ; // 11
counter . dec ( ) // 10
counter . dec ( 10 ) // 0
` ` `
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
# # Calls per second
` ` `
var fps = Profiler . metric ( 'fps' )
function render ( ) {
fps . mark ( ) ;
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
` ` `
* /
var MAX _HISTORY = 1024 ;
function Profiler ( ) { }
Profiler . metrics = { } ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
Profiler . get = function ( name ) {
return Profiler . metrics [ name ] || {
max : 0 ,
min : Number . MAX _VALUE ,
avg : 0 ,
total : 0 ,
count : 0 ,
history : typeof ( Float32Array ) !== 'undefined' ? new Float32Array ( MAX _HISTORY ) : [ ]
} ;
2013-08-29 21:22:57 +08:00
} ;
2014-12-10 01:05:11 +08:00
Profiler . new _value = function ( name , value ) {
var t = Profiler . metrics [ name ] = Profiler . get ( name ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
t . max = Math . max ( t . max , value ) ;
t . min = Math . min ( t . min , value ) ;
t . total += value ;
++ t . count ;
t . avg = t . total / t . count ;
t . history [ t . count % MAX _HISTORY ] = value ;
2013-08-29 21:22:57 +08:00
} ;
2014-12-10 01:05:11 +08:00
Profiler . print _stats = function ( ) {
for ( k in Profiler . metrics ) {
var t = Profiler . metrics [ k ] ;
console . log ( " === " + k + " === " ) ;
console . log ( " max: " + t . max ) ;
console . log ( " min: " + t . min ) ;
console . log ( " avg: " + t . avg ) ;
console . log ( " count: " + t . count ) ;
console . log ( " total: " + t . total ) ;
}
2013-08-29 21:22:57 +08:00
} ;
2014-12-10 01:05:11 +08:00
function Metric ( name ) {
this . t0 = null ;
this . name = name ;
this . count = 0 ;
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
Metric . prototype = {
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
//
// start a time measurement
//
start : function ( ) {
this . t0 = + new Date ( ) ;
return this ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// elapsed time since start was called
_elapsed : function ( ) {
return + new Date ( ) - this . t0 ;
2013-07-30 16:45:35 +08:00
} ,
2014-12-10 01:05:11 +08:00
//
// finish a time measurement and register it
// ``start`` should be called first, if not this
// function does not take effect
//
end : function ( ) {
if ( this . t0 !== null ) {
Profiler . new _value ( this . name , this . _elapsed ( ) ) ;
this . t0 = null ;
2013-10-29 01:41:27 +08:00
}
2013-07-30 16:45:35 +08:00
} ,
2014-12-10 01:05:11 +08:00
//
// increments the value
// qty: how many, default = 1
//
inc : function ( qty ) {
qty = qty === undefined ? 1 : qty ;
Profiler . new _value ( this . name , Profiler . get ( this . name ) . count + ( qty ? qty : 0 ) ) ;
2013-09-28 00:56:03 +08:00
} ,
2014-12-10 01:05:11 +08:00
//
// decrements the value
// qty: how many, default = 1
//
dec : function ( qty ) {
qty = qty === undefined ? 1 : qty ;
this . inc ( - qty ) ;
2013-10-11 17:09:30 +08:00
} ,
2014-12-10 01:05:11 +08:00
//
// measures how many times per second this function is called
//
mark : function ( ) {
++ this . count ;
if ( this . t0 === null ) {
this . start ( ) ;
return ;
}
var elapsed = this . _elapsed ( ) ;
if ( elapsed > 1 ) {
Profiler . new _value ( this . name , this . count ) ;
this . count = 0 ;
this . start ( ) ;
}
}
} ;
Profiler . metric = function ( name ) {
return new Metric ( name ) ;
} ;
module . exports = Profiler ;
} , { } ] , 18 : [ function ( require , module , exports ) {
module . exports = {
json : require ( './json' ) ,
JsonArray : require ( './jsonarray' ) ,
windshaft : require ( './windshaft' )
} ;
} , { "./json" : 19 , "./jsonarray" : 20 , "./windshaft" : 21 } ] , 19 : [ function ( require , module , exports ) {
var torque = require ( '../' ) ;
var Profiler = require ( '../profiler' ) ;
var Uint8Array = torque . types . Uint8Array ;
var Int32Array = torque . types . Int32Array ;
var Uint32Array = torque . types . Uint32Array ;
// format('hello, {0}', 'rambo') -> "hello, rambo"
function format ( str ) {
for ( var i = 1 ; i < arguments . length ; ++ i ) {
var attrs = arguments [ i ] ;
for ( var attr in attrs ) {
str = str . replace ( RegExp ( '\\{' + attr + '\\}' , 'g' ) , attrs [ attr ] ) ;
}
}
return str ;
}
var json = function ( options ) {
this . _ready = false ;
this . _tileQueue = [ ] ;
this . options = options ;
this . options . is _time = this . options . is _time === undefined ? true : this . options . is _time ;
this . options . tiler _protocol = options . tiler _protocol || 'http' ;
this . options . tiler _domain = options . tiler _domain || 'cartodb.com' ;
this . options . tiler _port = options . tiler _port || 80 ;
if ( this . options . data _aggregation ) {
this . options . cumulative = this . options . data _aggregation === 'cumulative' ;
}
// check options
if ( options . resolution === undefined ) throw new Error ( "resolution should be provided" ) ;
if ( options . steps === undefined ) throw new Error ( "steps should be provided" ) ;
if ( options . start === undefined ) {
this . _fetchKeySpan ( ) ;
} else {
this . _setReady ( true ) ;
}
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
json . prototype = {
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* return the torque tile encoded in an efficient javascript
* structure :
* {
* x : Uint8Array x coordinates in tile reference system , normally from 0 - 255
* y : Uint8Array y coordinates in tile reference system
* Index : Array index to the properties
* }
* /
proccessTile : function ( rows , coord , zoom ) {
var r ;
var x = new Uint8Array ( rows . length ) ;
var y = new Uint8Array ( rows . length ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var prof _mem = Profiler . metric ( 'ProviderJSON:mem' ) ;
var prof _point _count = Profiler . metric ( 'ProviderJSON:point_count' ) ;
var prof _process _time = Profiler . metric ( 'ProviderJSON:process_time' ) . start ( )
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// count number of dates
var dates = 0 ;
var maxDateSlots = - 1 ;
for ( r = 0 ; r < rows . length ; ++ r ) {
var row = rows [ r ] ;
dates += row . dates _ _uint16 . length ;
for ( var d = 0 ; d < row . dates _ _uint16 . length ; ++ d ) {
maxDateSlots = Math . max ( maxDateSlots , row . dates _ _uint16 [ d ] ) ;
}
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( this . options . cumulative ) {
dates = ( 1 + maxDateSlots ) * rows . length ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var type = this . options . cumulative ? Uint32Array : Uint8Array ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// reserve memory for all the dates
var timeIndex = new Int32Array ( maxDateSlots + 1 ) ; //index-size
var timeCount = new Int32Array ( maxDateSlots + 1 ) ;
var renderData = new ( this . options . valueDataType || type ) ( dates ) ;
var renderDataPos = new Uint32Array ( dates ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
prof _mem . inc (
4 * maxDateSlots + // timeIndex
4 * maxDateSlots + // timeCount
dates + //renderData
dates * 4
) ; //renderDataPos
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
prof _point _count . inc ( rows . length ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var rowsPerSlot = { } ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// precache pixel positions
for ( var r = 0 ; r < rows . length ; ++ r ) {
var row = rows [ r ] ;
x [ r ] = row . x _ _uint8 * this . options . resolution ;
// fix value when it's in the tile EDGE
// TODO: this should be fixed in SQL query
if ( row . y _ _uint8 === - 1 ) {
y [ r ] = 0 ;
} else {
y [ r ] = row . y _ _uint8 * this . options . resolution ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var dates = row . dates _ _uint16 ;
var vals = row . vals _ _uint8 ;
if ( ! this . options . cumulative ) {
for ( var j = 0 , len = dates . length ; j < len ; ++ j ) {
var rr = rowsPerSlot [ dates [ j ] ] || ( rowsPerSlot [ dates [ j ] ] = [ ] ) ;
if ( this . options . cumulative ) {
vals [ j ] += prev _val ;
}
prev _val = vals [ j ] ;
rr . push ( [ r , vals [ j ] ] ) ;
}
} else {
var valByDate = { }
for ( var j = 0 , len = dates . length ; j < len ; ++ j ) {
valByDate [ dates [ j ] ] = vals [ j ] ;
}
var accum = 0 ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// extend the latest to the end
for ( var j = dates [ 0 ] ; j <= maxDateSlots ; ++ j ) {
var rr = rowsPerSlot [ j ] || ( rowsPerSlot [ j ] = [ ] ) ;
var v = valByDate [ j ] ;
if ( v ) {
accum += v ;
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
rr . push ( [ r , accum ] ) ;
}
/ * v a r l a s t D a t e S l o t = d a t e s [ d a t e s . l e n g t h - 1 ] ;
for ( var j = lastDateSlot + 1 ; j <= maxDateSlots ; ++ j ) {
var rr = rowsPerSlot [ j ] || ( rowsPerSlot [ j ] = [ ] ) ;
rr . push ( [ r , prev _val ] ) ;
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
* /
}
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
// for each timeslot search active buckets
var renderDataIndex = 0 ;
var timeSlotIndex = 0 ;
var i = 0 ;
for ( var i = 0 ; i <= maxDateSlots ; ++ i ) {
var c = 0 ;
var slotRows = rowsPerSlot [ i ]
if ( slotRows ) {
for ( var r = 0 ; r < slotRows . length ; ++ r ) {
var rr = slotRows [ r ] ;
++ c ;
renderDataPos [ renderDataIndex ] = rr [ 0 ]
renderData [ renderDataIndex ] = rr [ 1 ] ;
++ renderDataIndex ;
}
}
timeIndex [ i ] = timeSlotIndex ;
timeCount [ i ] = c ;
timeSlotIndex += c ;
}
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
prof _process _time . end ( ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
return {
x : x ,
y : y ,
z : zoom ,
coord : {
x : coord . x ,
y : coord . y ,
z : zoom
} ,
timeCount : timeCount ,
timeIndex : timeIndex ,
renderDataPos : renderDataPos ,
renderData : renderData ,
maxDate : maxDateSlots
} ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_host : function ( ) {
var opts = this . options ;
var port = opts . sql _api _port ;
var domain = ( ( opts . user _name || opts . user ) + '.' + ( opts . sql _api _domain || 'cartodb.com' ) ) + ( port ? ':' + port : '' ) ;
var protocol = opts . sql _api _protocol || 'http' ;
return this . options . url || protocol + '://' + domain + '/api/v2/sql' ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
url : function ( subhost ) {
var opts = this . options ;
var protocol = opts . sql _api _protocol || 'http' ;
if ( ! this . options . cdn _url ) {
return this . _host ( ) ;
}
var h = protocol + "://" ;
if ( subhost ) {
h += subhost + "." ;
}
var cdn _host = opts . cdn _url ;
if ( ! cdn _host . http && ! cdn _host . https ) {
throw new Error ( "cdn_host should contain http and/or https entries" ) ;
}
h += cdn _host [ protocol ] + "/" + ( opts . user _name || opts . user ) + '/api/v2/sql' ;
return h ;
} ,
2013-09-30 19:31:27 +08:00
2014-12-10 01:05:11 +08:00
_hash : function ( str ) {
var hash = 0 ;
if ( ! str || str . length == 0 ) return hash ;
for ( var i = 0 , l = str . length ; i < l ; ++ i ) {
hash = ( ( ( hash << 5 ) - hash ) + str . charCodeAt ( i ) ) | 0 ;
}
return hash ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_extraParams : function ( ) {
if ( this . options . extra _params ) {
var p = [ ] ;
for ( var k in this . options . extra _params ) {
var v = this . options . extra _params [ k ] ;
if ( v ) {
p . push ( k + "=" + encodeURIComponent ( v ) ) ;
}
}
return p . join ( '&' ) ;
}
return null ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
isHttps : function ( ) {
return this . options . sql _api _protocol && this . options . sql _api _protocol === 'https' ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// execute actual query
sql : function ( sql , callback , options ) {
options = options || { } ;
var subdomains = this . options . subdomains || '0123' ;
if ( this . isHttps ( ) ) {
subdomains = [ null ] ; // no subdomain
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var url ;
if ( options . no _cdn ) {
url = this . _host ( ) ;
} else {
url = this . url ( subdomains [ Math . abs ( this . _hash ( sql ) ) % subdomains . length ] ) ;
}
var extra = this . _extraParams ( ) ;
torque . net . get ( url + "?q=" + encodeURIComponent ( sql ) + ( extra ? "&" + extra : '' ) , function ( data ) {
if ( options . parseJSON ) {
data = JSON . parse ( data && data . responseText ) ;
}
callback && callback ( data ) ;
} ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getTileData : function ( coord , zoom , callback ) {
if ( ! this . _ready ) {
this . _tileQueue . push ( [ coord , zoom , callback ] ) ;
} else {
this . _getTileData ( coord , zoom , callback ) ;
}
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_setReady : function ( ready ) {
this . _ready = true ;
this . _processQueue ( ) ;
this . options . ready && this . options . ready ( ) ;
} ,
_processQueue : function ( ) {
var item ;
while ( item = this . _tileQueue . pop ( ) ) {
this . _getTileData . apply ( this , item ) ;
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* ` coord ` object like { x : tilex , y : tiley }
* ` zoom ` quadtree zoom level
* /
_getTileData : function ( coord , zoom , callback ) {
var prof _fetch _time = Profiler . metric ( 'ProviderJSON:tile_fetch_time' ) . start ( )
this . table = this . options . table ;
var numTiles = 1 << zoom ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var column _conv = this . options . column ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( this . options . is _time ) {
column _conv = format ( "date_part('epoch', {column})" , this . options ) ;
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
var sql = "" +
"WITH " +
"par AS (" +
" SELECT CDB_XYZ_Resolution({zoom})*{resolution} as res" +
", 256/{resolution} as tile_size" +
", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " +
")," +
"cte AS ( " +
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" +
", {countby} c" +
", floor(({column_conv} - {start})/{step}) d" +
" FROM ({_sql}) i, par p " +
" WHERE i.the_geom_webmercator && p.ext " +
" GROUP BY g, d" +
") " +
"" +
"SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8," +
" array_agg(c) vals__uint8," +
" array_agg(d) dates__uint16" +
// the tile_size where are needed because the overlaps query in cte subquery includes the points
// in the left and bottom borders of the tile
" FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8" ;
2013-08-29 21:22:57 +08:00
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
var query = format ( sql , this . options , {
zoom : zoom ,
x : coord . x ,
y : coord . y ,
column _conv : column _conv ,
_sql : this . getSQL ( )
} ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var self = this ;
this . sql ( query , function ( data ) {
if ( data ) {
var rows = JSON . parse ( data . responseText ) . rows ;
callback ( self . proccessTile ( rows , coord , zoom ) ) ;
} else {
callback ( null ) ;
}
prof _fetch _time . end ( ) ;
} ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getKeySpan : function ( ) {
return {
start : this . options . start * 1000 ,
end : this . options . end * 1000 ,
step : this . options . step ,
steps : this . options . steps ,
columnType : this . options . is _time ? 'date' : 'number'
} ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
setColumn : function ( column , isTime ) {
this . options . column = column ;
this . options . is _time = isTime === undefined ? true : false ;
this . reload ( ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
setResolution : function ( res ) {
this . options . resolution = res ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// return true if tiles has been changed
setOptions : function ( opt ) {
var refresh = false ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . resolution !== undefined && opt . resolution !== this . options . resolution ) {
this . options . resolution = opt . resolution ;
refresh = true ;
}
2013-12-12 21:03:35 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . steps !== undefined && opt . steps !== this . options . steps ) {
this . setSteps ( opt . steps , { silent : true } ) ;
refresh = true ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . column !== undefined && opt . column !== this . options . column ) {
this . options . column = opt . column ;
refresh = true ;
}
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . countby !== undefined && opt . countby !== this . options . countby ) {
this . options . countby = opt . countby ;
refresh = true ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . data _aggregation !== undefined ) {
var c = opt . data _aggregation === 'cumulative' ;
if ( this . options . cumulative !== c ) {
this . options . cumulative = c ;
refresh = true ;
}
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( refresh ) this . reload ( ) ;
return refresh ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
reload : function ( ) {
this . _ready = false ;
this . _fetchKeySpan ( ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
setSQL : function ( sql ) {
if ( this . options . sql != sql ) {
this . options . sql = sql ;
this . reload ( ) ;
}
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getSteps : function ( ) {
return Math . min ( this . options . steps , this . options . data _steps ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
setSteps : function ( steps , opt ) {
opt = opt || { } ;
if ( this . options . steps !== steps ) {
this . options . steps = steps ;
this . options . step = ( this . options . end - this . options . start ) / this . getSteps ( ) ;
this . options . step = this . options . step || 1 ;
if ( ! opt . silent ) this . reload ( ) ;
}
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getBounds : function ( ) {
return this . options . bounds ;
} ,
2013-09-30 19:31:27 +08:00
2014-12-10 01:05:11 +08:00
getSQL : function ( ) {
return this . options . sql || "select * from " + this . options . table ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_tilerHost : function ( ) {
var opts = this . options ;
var user = ( opts . user _name || opts . user ) ;
return opts . tiler _protocol +
"://" + ( user ? user + "." : "" ) +
opts . tiler _domain +
( ( opts . tiler _port != "" ) ? ( ":" + opts . tiler _port ) : "" ) ;
} ,
2014-01-09 23:56:06 +08:00
2014-12-10 01:05:11 +08:00
_fetchUpdateAt : function ( callback ) {
var self = this ;
var layergroup = {
"version" : "1.0.1" ,
"stat_tag" : this . options . stat _tag || 'torque' ,
"layers" : [ {
"type" : "cartodb" ,
"options" : {
"cartocss_version" : "2.1.1" ,
"cartocss" : "#layer {}" ,
"sql" : this . getSQL ( )
}
} ]
} ;
var url = this . _tilerHost ( ) + "/tiles/layergroup" ;
var extra = this . _extraParams ( ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// tiler needs map_key instead of api_key
// so replace it
if ( extra ) {
extra = extra . replace ( 'api_key=' , 'map_key=' ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
url = url +
"?config=" + encodeURIComponent ( JSON . stringify ( layergroup ) ) +
"&callback=?" + ( extra ? "&" + extra : '' ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
torque . net . jsonp ( url , function ( data ) {
var query = format ( "select * from ({sql}) __torque_wrap_sql limit 0" , { sql : self . getSQL ( ) } ) ;
self . sql ( query , function ( queryData ) {
if ( data ) {
callback ( {
updated _at : data . last _updated ,
fields : queryData . fields
} ) ;
}
} , { parseJSON : true } ) ;
} ) ;
} ,
2013-10-31 21:40:01 +08:00
2014-12-10 01:05:11 +08:00
//
// the data range could be set by the user though ``start``
// option. It can be fecthed from the table when the start
// is not specified.
//
_fetchKeySpan : function ( ) {
var self = this ;
var max _col , min _col , max _tmpl , min _tmpl ;
2013-10-31 21:40:01 +08:00
2014-12-10 01:05:11 +08:00
this . _fetchUpdateAt ( function ( data ) {
if ( ! data ) return ;
self . options . extra _params = self . options . extra _params || { } ;
self . options . extra _params . last _updated = data . updated _at || 0 ;
self . options . extra _params . cache _policy = 'persist' ;
self . options . is _time = data . fields [ self . options . column ] . type === 'date' ;
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
var column _conv = self . options . column ;
if ( self . options . is _time ) {
max _tmpl = "date_part('epoch', max({column}))" ;
min _tmpl = "date_part('epoch', min({column}))" ;
column _conv = format ( "date_part('epoch', {column})" , self . options ) ;
} else {
max _tmpl = "max({column})" ;
min _tmpl = "min({column})" ;
}
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
max _col = format ( max _tmpl , { column : self . options . column } ) ;
min _col = format ( min _tmpl , { column : self . options . column } ) ;
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
/ * v a r s q l _ s t a t s = " " +
"WITH summary_groups as ( " +
"WITH summary as ( " +
"select (row_number() over (order by __time_col asc nulls last)+1)/2 as rownum, __time_col " +
"from (select *, {column} as __time_col from ({sql}) __s) __torque_wrap_sql " +
"order by __time_col asc " +
") " +
"SELECT " +
"max(__time_col) OVER(PARTITION BY rownum) - " +
"min(__time_col) OVER(PARTITION BY rownum) diff " +
"FROM summary " +
"), subq as ( " +
" SELECT " +
"st_xmax(st_envelope(st_collect(the_geom))) xmax, " +
"st_ymax(st_envelope(st_collect(the_geom))) ymax, " +
"st_xmin(st_envelope(st_collect(the_geom))) xmin, " +
"st_ymin(st_envelope(st_collect(the_geom))) ymin, " +
"{max_col} max, " +
"{min_col} min FROM ({sql}) __torque_wrap_sql " +
")" +
"SELECT " +
"xmax, xmin, ymax, ymin, a.max as max_date, a.min as min_date, " +
"avg(diff) as diffavg," +
"(a.max - a.min)/avg(diff) as num_steps " +
"FROM summary_groups, subq a " +
"WHERE diff > 0 group by xmax, xmin, ymax, ymin, max_date, min_date" ;
* /
var sql _stats = " SELECT " +
"st_xmax(st_envelope(st_collect(the_geom))) xmax, " +
"st_ymax(st_envelope(st_collect(the_geom))) ymax, " +
"st_xmin(st_envelope(st_collect(the_geom))) xmin, " +
"st_ymin(st_envelope(st_collect(the_geom))) ymin, " +
"count(*) as num_steps, " +
"{max_col} max_date, " +
"{min_col} min_date FROM ({sql}) __torque_wrap_sql " ;
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
var sql = format ( sql _stats , {
max _col : max _col ,
min _col : min _col ,
column : column _conv ,
sql : self . getSQL ( )
} ) ;
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
self . sql ( sql , function ( data ) {
//TODO: manage bounds
data = data . rows [ 0 ] ;
self . options . start = data . min _date ;
self . options . end = data . max _date ;
self . options . step = ( data . max _date - data . min _date ) / Math . min ( self . options . steps , data . num _steps >> 0 ) ;
self . options . data _steps = data . num _steps >> 0 ;
// step can't be 0
self . options . step = self . options . step || 1 ;
self . options . bounds = [
[ data . ymin , data . xmin ] ,
[ data . ymax , data . xmax ]
] ;
self . _setReady ( true ) ;
} , { parseJSON : true , no _cdn : true } ) ;
} , { parseJSON : true , no _cdn : true } )
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
module . exports = json ;
2013-10-31 21:40:01 +08:00
2014-12-10 01:05:11 +08:00
} , { "../" : 10 , "../profiler" : 17 } ] , 20 : [ function ( require , module , exports ) {
var torque = require ( '../' ) ;
var Profiler = require ( '../profiler' ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var Uint8Array = torque . types . Uint8Array ;
var Int32Array = torque . types . Int32Array ;
var Uint32Array = torque . types . Uint32Array ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// format('hello, {0}', 'rambo') -> "hello, rambo"
function format ( str , attrs ) {
for ( var i = 1 ; i < arguments . length ; ++ i ) {
var attrs = arguments [ i ] ;
for ( var attr in attrs ) {
str = str . replace ( RegExp ( '\\{' + attr + '\\}' , 'g' ) , attrs [ attr ] ) ;
}
2014-09-24 19:12:44 +08:00
}
2014-12-10 01:05:11 +08:00
return str ;
}
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
var json = function ( options ) {
// check options
this . options = options ;
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
json . prototype = {
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
//
// return the data aggregated by key:
// {
// key0: 12,
// key1: 32
// key2: 25
// }
//
aggregateByKey : function ( rows ) {
function getKeys ( row ) {
var HEADER _SIZE = 3 ;
var valuesCount = row . data [ 2 ] ;
var keys = { } ;
for ( var s = 0 ; s < valuesCount ; ++ s ) {
keys [ row . data [ HEADER _SIZE + s ] ] = row . data [ HEADER _SIZE + valuesCount + s ] ;
}
return keys ;
}
var keys = { } ;
for ( r = 0 ; r < rows . length ; ++ r ) {
var rowKeys = getKeys ( rows [ r ] ) ;
for ( var k in rowKeys ) {
keys [ k ] = keys [ k ] || 0 ;
keys [ k ] += rowKeys [ k ] ;
}
}
return keys ;
} ,
2013-12-12 21:03:35 +08:00
2013-09-11 19:21:33 +08:00
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
/ * *
*
* /
proccessTile : function ( rows , coord , zoom ) {
var r ;
var x = new Uint8Array ( rows . length ) ;
var y = new Uint8Array ( rows . length ) ;
var self = this ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// decode into a javascript strcuture the array
function decode _row ( row ) {
var HEADER _SIZE = 3 ;
var o = {
x : row . data [ 0 ] * self . options . resolution ,
y : row . data [ 1 ] * self . options . resolution ,
valuesCount : row . data [ 2 ] ,
times : [ ] ,
values : [ ]
} ;
for ( var s = 0 ; s < o . valuesCount ; ++ s ) {
o . times . push ( row . data [ HEADER _SIZE + s ] ) ;
o . values . push ( row . data [ HEADER _SIZE + o . valuesCount + s ] ) ;
}
if ( self . options . cumulative ) {
for ( var s = 1 ; s < o . valuesCount ; ++ s ) {
o . values [ s ] += o . values [ s - 1 ] ;
}
}
return o
}
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// decode all the rows
for ( r = 0 ; r < rows . length ; ++ r ) {
rows [ r ] = decode _row ( rows [ r ] ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// count number of dates
var dates = 0 ;
var maxDateSlots = 0 ;
for ( r = 0 ; r < rows . length ; ++ r ) {
var row = rows [ r ] ;
dates += row . times . length ;
for ( var d = 0 ; d < row . times . length ; ++ d ) {
maxDateSlots = Math . max ( maxDateSlots , row . times [ d ] ) ;
}
}
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
// reserve memory for all the dates
var timeIndex = new Int32Array ( maxDateSlots + 1 ) ; //index-size
var timeCount = new Int32Array ( maxDateSlots + 1 ) ;
var renderData = new ( this . options . valueDataType || Uint8Array ) ( dates ) ;
var renderDataPos = new Uint32Array ( dates ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var rowsPerSlot = { } ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// precache pixel positions
for ( var r = 0 ; r < rows . length ; ++ r ) {
var row = rows [ r ] ;
x [ r ] = row . x ;
y [ r ] = row . y ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var dates = row . times ;
var vals = row . values ;
for ( var j = 0 , len = dates . length ; j < len ; ++ j ) {
var rr = rowsPerSlot [ dates [ j ] ] || ( rowsPerSlot [ dates [ j ] ] = [ ] ) ;
rr . push ( [ r , vals [ j ] ] ) ;
}
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// for each timeslot search active buckets
var renderDataIndex = 0 ;
var timeSlotIndex = 0 ;
var i = 0 ;
for ( var i = 0 ; i <= maxDateSlots ; ++ i ) {
var c = 0 ;
var slotRows = rowsPerSlot [ i ]
if ( slotRows ) {
for ( var r = 0 ; r < slotRows . length ; ++ r ) {
var rr = slotRows [ r ] ;
++ c ;
renderDataPos [ renderDataIndex ] = rr [ 0 ]
renderData [ renderDataIndex ] = rr [ 1 ] ;
++ renderDataIndex ;
}
}
timeIndex [ i ] = timeSlotIndex ;
timeCount [ i ] = c ;
timeSlotIndex += c ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
return {
x : x ,
y : y ,
coord : {
x : coord . x ,
y : coord . y ,
z : zoom
} ,
timeCount : timeCount ,
timeIndex : timeIndex ,
renderDataPos : renderDataPos ,
renderData : renderData
} ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
url : function ( ) {
return this . options . url ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
tileUrl : function ( coord , zoom ) {
var template = this . url ( ) ;
var s = ( this . options . subdomains || 'abcd' ) [ ( coord . x + coord . y + zoom ) % 4 ] ;
return template
. replace ( '{x}' , coord . x )
. replace ( '{y}' , coord . y )
. replace ( '{z}' , zoom )
. replace ( '{s}' , s ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getTile : function ( coord , zoom , callback ) {
var template = this . tileUrl ( coord , zoom ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var self = this ;
var fetchTime = Profiler . metric ( 'jsonarray:fetch time' ) ;
fetchTime . start ( ) ;
torque . net . get ( template , function ( data ) {
fetchTime . end ( ) ;
if ( data ) {
data = JSON . parse ( data . responseText ) ;
}
callback ( data ) ;
} ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* ` coord ` object like { x : tilex , y : tiley }
* ` zoom ` quadtree zoom level
* /
getTileData : function ( coord , zoom , callback ) {
var template = this . tileUrl ( coord , zoom ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var self = this ;
var fetchTime = Profiler . metric ( 'jsonarray:fetch time' ) ;
fetchTime . start ( ) ;
torque . net . get ( template , function ( data ) {
fetchTime . end ( ) ;
var processed = null ;
var processingTime = Profiler . metric ( 'jsonarray:processing time' ) ;
var parsingTime = Profiler . metric ( 'jsonarray:parsing time' ) ;
try {
processingTime . start ( ) ;
parsingTime . start ( ) ;
var rows = JSON . parse ( data . responseText || data . response ) . rows ;
parsingTime . end ( ) ;
processed = self . proccessTile ( rows , coord , zoom ) ;
processingTime . end ( ) ;
} catch ( e ) {
console . error ( "problem parsing JSON on " , coord , zoom ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
callback ( processed ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
module . exports = json ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} , { "../" : 10 , "../profiler" : 17 } ] , 21 : [ function ( require , module , exports ) {
var torque = require ( '../' ) ;
var Profiler = require ( '../profiler' ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var Uint8Array = torque . types . Uint8Array ;
var Int32Array = torque . types . Int32Array ;
var Uint32Array = torque . types . Uint32Array ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// format('hello, {0}', 'rambo') -> "hello, rambo"
function format ( str ) {
for ( var i = 1 ; i < arguments . length ; ++ i ) {
var attrs = arguments [ i ] ;
for ( var attr in attrs ) {
str = str . replace ( RegExp ( '\\{' + attr + '\\}' , 'g' ) , attrs [ attr ] ) ;
}
}
return str ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
var json = function ( options ) {
this . _ready = false ;
this . _tileQueue = [ ] ;
this . options = options ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
this . options . is _time = this . options . is _time === undefined ? true : this . options . is _time ;
this . options . tiler _protocol = options . tiler _protocol || 'http' ;
this . options . tiler _domain = options . tiler _domain || 'cartodb.com' ;
this . options . tiler _port = options . tiler _port || 80 ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( this . options . data _aggregation ) {
this . options . cumulative = this . options . data _aggregation === 'cumulative' ;
}
if ( this . options . auth _token ) {
var e = this . options . extra _params || ( this . options . extra _params = { } ) ;
e . auth _token = this . options . auth _token ;
}
2014-12-19 18:01:04 +08:00
if ( ! this . options . no _fetch _map ) {
this . _fetchMap ( ) ;
}
2014-12-10 01:05:11 +08:00
} ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
json . prototype = {
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* return the torque tile encoded in an efficient javascript
* structure :
* {
* x : Uint8Array x coordinates in tile reference system , normally from 0 - 255
* y : Uint8Array y coordinates in tile reference system
* Index : Array index to the properties
* }
* /
proccessTile : function ( rows , coord , zoom ) {
var r ;
var x = new Uint8Array ( rows . length ) ;
var y = new Uint8Array ( rows . length ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var prof _mem = Profiler . metric ( 'torque.provider.windshaft.mem' ) ;
var prof _point _count = Profiler . metric ( 'torque.provider.windshaft.points' ) ;
var prof _process _time = Profiler . metric ( 'torque.provider.windshaft.process_time' ) . start ( ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// count number of dates
var dates = 0 ;
var maxDateSlots = - 1 ;
for ( r = 0 ; r < rows . length ; ++ r ) {
var row = rows [ r ] ;
dates += row . dates _ _uint16 . length ;
for ( var d = 0 ; d < row . dates _ _uint16 . length ; ++ d ) {
maxDateSlots = Math . max ( maxDateSlots , row . dates _ _uint16 [ d ] ) ;
}
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
if ( this . options . cumulative ) {
dates = ( 1 + maxDateSlots ) * rows . length ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var type = this . options . cumulative ? Uint32Array : Uint8Array ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// reserve memory for all the dates
var timeIndex = new Int32Array ( maxDateSlots + 1 ) ; //index-size
var timeCount = new Int32Array ( maxDateSlots + 1 ) ;
var renderData = new ( this . options . valueDataType || type ) ( dates ) ;
var renderDataPos = new Uint32Array ( dates ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
prof _mem . inc (
4 * maxDateSlots + // timeIndex
4 * maxDateSlots + // timeCount
dates + //renderData
dates * 4
) ; //renderDataPos
2013-09-28 00:56:03 +08:00
2014-12-10 01:05:11 +08:00
prof _point _count . inc ( rows . length ) ;
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
var rowsPerSlot = { } ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// precache pixel positions
for ( var r = 0 ; r < rows . length ; ++ r ) {
var row = rows [ r ] ;
x [ r ] = row . x _ _uint8 * this . options . resolution ;
// fix value when it's in the tile EDGE
// TODO: this should be fixed in SQL query
if ( row . y _ _uint8 === - 1 ) {
y [ r ] = 0 ;
} else {
y [ r ] = row . y _ _uint8 * this . options . resolution ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var dates = row . dates _ _uint16 ;
var vals = row . vals _ _uint8 ;
if ( ! this . options . cumulative ) {
for ( var j = 0 , len = dates . length ; j < len ; ++ j ) {
var rr = rowsPerSlot [ dates [ j ] ] || ( rowsPerSlot [ dates [ j ] ] = [ ] ) ;
if ( this . options . cumulative ) {
vals [ j ] += prev _val ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
prev _val = vals [ j ] ;
rr . push ( [ r , vals [ j ] ] ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
} else {
var valByDate = { }
for ( var j = 0 , len = dates . length ; j < len ; ++ j ) {
valByDate [ dates [ j ] ] = vals [ j ] ;
}
var accum = 0 ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// extend the latest to the end
for ( var j = dates [ 0 ] ; j <= maxDateSlots ; ++ j ) {
var rr = rowsPerSlot [ j ] || ( rowsPerSlot [ j ] = [ ] ) ;
var v = valByDate [ j ] ;
if ( v ) {
accum += v ;
}
rr . push ( [ r , accum ] ) ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * v a r l a s t D a t e S l o t = d a t e s [ d a t e s . l e n g t h - 1 ] ;
for ( var j = lastDateSlot + 1 ; j <= maxDateSlots ; ++ j ) {
var rr = rowsPerSlot [ j ] || ( rowsPerSlot [ j ] = [ ] ) ;
rr . push ( [ r , prev _val ] ) ;
}
* /
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
// for each timeslot search active buckets
var renderDataIndex = 0 ;
var timeSlotIndex = 0 ;
var i = 0 ;
for ( var i = 0 ; i <= maxDateSlots ; ++ i ) {
var c = 0 ;
var slotRows = rowsPerSlot [ i ]
if ( slotRows ) {
for ( var r = 0 ; r < slotRows . length ; ++ r ) {
var rr = slotRows [ r ] ;
++ c ;
renderDataPos [ renderDataIndex ] = rr [ 0 ]
renderData [ renderDataIndex ] = rr [ 1 ] ;
++ renderDataIndex ;
}
}
timeIndex [ i ] = timeSlotIndex ;
timeCount [ i ] = c ;
timeSlotIndex += c ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
prof _process _time . end ( ) ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
return {
x : x ,
y : y ,
z : zoom ,
coord : {
x : coord . x ,
y : coord . y ,
z : zoom
} ,
timeCount : timeCount ,
timeIndex : timeIndex ,
renderDataPos : renderDataPos ,
renderData : renderData ,
maxDate : maxDateSlots
} ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * s e t C a r t o C S S : f u n c t i o n ( c ) {
this . options . cartocss = c ;
} , * /
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setSteps : function ( steps , opt ) {
opt = opt || { } ;
if ( this . options . steps !== steps ) {
this . options . steps = steps ;
this . options . step = ( this . options . end - this . options . start ) / this . getSteps ( ) ;
this . options . step = this . options . step || 1 ;
if ( ! opt . silent ) this . reload ( ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setOptions : function ( opt ) {
var refresh = false ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . resolution !== undefined && opt . resolution !== this . options . resolution ) {
this . options . resolution = opt . resolution ;
refresh = true ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . steps !== undefined && opt . steps !== this . options . steps ) {
this . setSteps ( opt . steps , { silent : true } ) ;
refresh = true ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . column !== undefined && opt . column !== this . options . column ) {
this . options . column = opt . column ;
refresh = true ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
if ( opt . countby !== undefined && opt . countby !== this . options . countby ) {
this . options . countby = opt . countby ;
refresh = true ;
}
2013-09-30 22:56:29 +08:00
2014-12-10 01:05:11 +08:00
if ( opt . data _aggregation !== undefined ) {
var c = opt . data _aggregation === 'cumulative' ;
if ( this . options . cumulative !== c ) {
this . options . cumulative = c ;
refresh = true ;
}
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
if ( refresh ) this . reload ( ) ;
return refresh ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_extraParams : function ( e ) {
e = torque . extend ( torque . extend ( { } , e ) , this . options . extra _params ) ;
if ( e ) {
var p = [ ] ;
for ( var k in e ) {
var v = e [ k ] ;
if ( v ) {
if ( torque . isArray ( v ) ) {
for ( var i = 0 , len = v . length ; i < len ; i ++ ) {
p . push ( k + "[]=" + encodeURIComponent ( v [ i ] ) ) ;
}
} else {
p . push ( k + "=" + encodeURIComponent ( v ) ) ;
}
}
}
return p . join ( '&' ) ;
}
return null ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getTileData : function ( coord , zoom , callback ) {
if ( ! this . _ready ) {
this . _tileQueue . push ( [ coord , zoom , callback ] ) ;
} else {
this . _getTileData ( coord , zoom , callback ) ;
}
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_setReady : function ( ready ) {
this . _ready = true ;
this . _processQueue ( ) ;
this . options . ready && this . options . ready ( ) ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
_processQueue : function ( ) {
var item ;
while ( item = this . _tileQueue . pop ( ) ) {
this . _getTileData . apply ( this , item ) ;
}
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* ` coord ` object like { x : tilex , y : tiley }
* ` zoom ` quadtree zoom level
* /
_getTileData : function ( coord , zoom , callback ) {
var self = this ;
var prof _fetch _time = Profiler . metric ( 'torque.provider.windshaft.tile.fetch' ) . start ( ) ;
var subdomains = this . options . subdomains || '0123' ;
2015-03-02 18:56:25 +08:00
var limit _x = Math . pow ( 2 , zoom ) ;
var corrected _x = ( ( coord . x % limit _x ) + limit _x ) % limit _x ;
var index = Math . abs ( corrected _x + coord . y ) % subdomains . length ;
2014-12-10 01:05:11 +08:00
var url = this . templateUrl
2015-03-02 18:56:25 +08:00
. replace ( '{x}' , corrected _x )
. replace ( '{y}' , coord . y )
2014-12-10 01:05:11 +08:00
. replace ( '{z}' , zoom )
. replace ( '{s}' , subdomains [ index ] )
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var extra = this . _extraParams ( ) ;
torque . net . get ( url + ( extra ? "?" + extra : '' ) , function ( data ) {
prof _fetch _time . end ( ) ;
if ( data && data . responseText ) {
var rows = JSON . parse ( data . responseText ) ;
callback ( self . proccessTile ( rows , coord , zoom ) ) ;
} else {
Profiler . metric ( 'torque.provider.windshaft.tile.error' ) . inc ( ) ;
callback ( null ) ;
}
} ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getKeySpan : function ( ) {
return {
start : this . options . start ,
end : this . options . end ,
step : this . options . step ,
steps : this . options . steps ,
columnType : this . options . column _type
} ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
setColumn : function ( column , isTime ) {
this . options . column = column ;
this . options . is _time = isTime === undefined ? true : false ;
this . reload ( ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
reload : function ( ) {
this . _ready = false ;
this . _fetchMap ( ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getSteps : function ( ) {
return Math . min ( this . options . steps , this . options . data _steps ) ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
getBounds : function ( ) {
return this . options . bounds ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
getSQL : function ( ) {
return this . options . sql || "select * from " + this . options . table ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
setSQL : function ( sql ) {
if ( this . options . sql != sql ) {
this . options . sql = sql ;
this . reload ( ) ;
}
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
_tilerHost : function ( ) {
var opts = this . options ;
var user = ( opts . user _name || opts . user ) ;
return opts . tiler _protocol +
"://" + ( user ? user + "." : "" ) +
opts . tiler _domain +
( ( opts . tiler _port != "" ) ? ( ":" + opts . tiler _port ) : "" ) ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
url : function ( ) {
var opts = this . options ;
var protocol = opts . tiler _protocol || 'http' ;
if ( ! this . options . cdn _url || this . options . no _cdn ) {
return this . _tilerHost ( ) ;
}
var h = protocol + "://"
if ( protocol === 'http' ) {
h += "{s}." ;
}
var cdn _host = opts . cdn _url ;
if ( ! cdn _host . http && ! cdn _host . https ) {
throw new Error ( "cdn_host should contain http and/or https entries" ) ;
}
h += cdn _host [ protocol ] + "/" + ( opts . user _name || opts . user ) ;
return h ;
} ,
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
_generateCartoCSS : function ( ) {
var attr = {
'-torque-frame-count' : this . options . steps ,
'-torque-resolution' : this . options . resolution ,
'-torque-aggregation-function' : "'" + this . options . countby + "'" ,
'-torque-time-attribute' : "'" + this . options . column + "'" ,
'-torque-data-aggregation' : this . options . cumulative ? 'cumulative' : 'linear' ,
} ;
var st = 'Map{' ;
for ( var k in attr ) {
st += k + ":" + attr [ k ] + ";" ;
}
return st + "}" ;
} ,
2014-05-22 23:25:22 +08:00
2014-12-10 01:05:11 +08:00
_fetchMap : function ( callback ) {
var self = this ;
var layergroup = { } ;
var host = this . options . dynamic _cdn ? this . url ( ) . replace ( '{s}' , '0' ) : this . _tilerHost ( ) ;
var url = host + "/api/v1/map" ;
var named = this . options . named _map ;
2015-03-02 18:56:25 +08:00
var allParams = { } ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
if ( named ) {
//tiles/template
url = host + "/api/v1/map/named/" + named . name + "/jsonp" ;
2015-03-02 18:56:25 +08:00
if ( typeof named . params !== "undefined" ) {
layergroup = named . params ;
}
2014-12-10 01:05:11 +08:00
} else {
layergroup = {
"version" : "1.0.1" ,
"stat_tag" : this . options . stat _tag || 'torque' ,
"layers" : [ {
"type" : "torque" ,
"options" : {
"cartocss_version" : "1.0.0" ,
"cartocss" : this . _generateCartoCSS ( ) ,
"sql" : this . getSQL ( )
}
} ]
} ;
}
2015-03-02 18:56:25 +08:00
if ( this . options . stat _tag ) {
allParams [ "stat_tag" ] = this . options . stat _tag ;
}
extra = this . _extraParams ( allParams ) ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
// tiler needs map_key instead of api_key
// so replace it
if ( extra ) {
extra = extra . replace ( 'api_key=' , 'map_key=' ) ;
}
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
url = url +
"?config=" + encodeURIComponent ( JSON . stringify ( layergroup ) ) +
"&callback=?" + ( extra ? "&" + extra : '' ) ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
var map _instance _time = Profiler . metric ( 'torque.provider.windshaft.layergroup.time' ) . start ( ) ;
torque . net . jsonp ( url , function ( data ) {
map _instance _time . end ( ) ;
if ( data ) {
var torque _key = Object . keys ( data . metadata . torque ) [ 0 ]
var opt = data . metadata . torque [ torque _key ] ;
for ( var k in opt ) {
self . options [ k ] = opt [ k ] ;
}
// use cdn_url if present
if ( data . cdn _url ) {
var c = self . options . cdn _url = self . options . cdn _url || { } ;
c . http = data . cdn _url . http || c . http ;
c . https = data . cdn _url . https || c . https ;
}
self . templateUrl = self . url ( ) + "/api/v1/map/" + data . layergroupid + "/" + torque _key + "/{z}/{x}/{y}.json.torque" ;
self . _setReady ( true ) ;
} else {
Profiler . metric ( 'torque.provider.windshaft.layergroup.error' ) . inc ( ) ;
}
} , { callbackName : self . options . instanciateCallback } ) ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} ;
2013-12-18 19:06:14 +08:00
2014-12-10 01:05:11 +08:00
module . exports = json ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} , { "../" : 10 , "../profiler" : 17 } ] , 22 : [ function ( require , module , exports ) {
var TAU = Math . PI * 2 ;
// min value to render a line.
// it does not make sense to render a line of a width is not even visible
var LINEWIDTH _MIN _VALUE = 0.05 ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
function renderPoint ( ctx , st ) {
ctx . fillStyle = st [ 'marker-fill' ] ;
var pixel _size = st [ 'marker-width' ] ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// render a circle
2014-12-19 18:01:04 +08:00
// TODO: fill and stroke order should depend on the order of the properties
// in the cartocss.
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// fill
ctx . beginPath ( ) ;
ctx . arc ( 0 , 0 , pixel _size , 0 , TAU , true , true ) ;
ctx . closePath ( ) ;
if ( st [ 'marker-fill' ] ) {
2014-12-17 22:34:45 +08:00
if ( st [ 'marker-fill-opacity' ] !== undefined || st [ 'marker-opacity' ] !== undefined ) {
ctx . globalAlpha = st [ 'marker-fill-opacity' ] || st [ 'marker-opacity' ] ;
2014-12-10 01:05:11 +08:00
}
ctx . fill ( ) ;
2014-03-21 22:57:51 +08:00
}
2014-01-09 23:56:06 +08:00
2014-12-10 01:05:11 +08:00
// stroke
ctx . globalAlpha = 1.0 ;
2014-12-19 18:01:04 +08:00
if ( st [ 'marker-line-color' ] && st [ 'marker-line-width' ] && st [ 'marker-line-width' ] > LINEWIDTH _MIN _VALUE ) {
if ( st [ 'marker-line-opacity' ] !== undefined ) {
2014-12-10 01:05:11 +08:00
ctx . globalAlpha = st [ 'marker-line-opacity' ] ;
}
2014-12-19 18:01:04 +08:00
if ( st [ 'marker-line-width' ] !== undefined ) {
ctx . lineWidth = st [ 'marker-line-width' ] ;
2014-12-10 01:05:11 +08:00
}
2014-12-19 18:01:04 +08:00
ctx . strokeStyle = st [ 'marker-line-color' ] ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
// do not render for alpha = 0
if ( ctx . globalAlpha > 0 ) {
ctx . stroke ( ) ;
}
}
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
function renderRectangle ( ctx , st ) {
ctx . fillStyle = st [ 'marker-fill' ] ;
var pixel _size = st [ 'marker-width' ] ;
var w = pixel _size * 2 ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
// fill
2014-12-17 22:34:45 +08:00
if ( st [ 'marker-fill' ] ) {
if ( st [ 'marker-fill-opacity' ] !== undefined || st [ 'marker-opacity' ] !== undefined ) {
ctx . globalAlpha = st [ 'marker-fill-opacity' ] || st [ 'marker-opacity' ] ;
}
ctx . fillRect ( - pixel _size , - pixel _size , w , w )
2014-12-10 01:05:11 +08:00
}
// stroke
ctx . globalAlpha = 1.0 ;
2014-12-19 18:01:04 +08:00
if ( st [ 'marker-line-color' ] && st [ 'marker-line-width' ] ) {
2014-12-10 01:05:11 +08:00
if ( st [ 'marker-line-opacity' ] ) {
ctx . globalAlpha = st [ 'marker-line-opacity' ] ;
}
2014-12-19 18:01:04 +08:00
if ( st [ 'marker-line-width' ] ) {
ctx . lineWidth = st [ 'marker-line-width' ] ;
2014-12-10 01:05:11 +08:00
}
2014-12-19 18:01:04 +08:00
ctx . strokeStyle = st [ 'marker-line-color' ] ;
2014-12-10 01:05:11 +08:00
// do not render for alpha = 0
if ( ctx . globalAlpha > 0 ) {
ctx . strokeRect ( - pixel _size , - pixel _size , w , w )
}
2014-03-21 22:57:51 +08:00
}
2014-12-10 01:05:11 +08:00
}
2013-08-29 21:22:57 +08:00
2015-03-02 18:56:25 +08:00
function renderSprite ( ctx , img , st ) {
2015-01-14 23:51:32 +08:00
if ( img . complete ) {
2015-03-02 18:56:25 +08:00
if ( st [ 'marker-fill-opacity' ] !== undefined || st [ 'marker-opacity' ] !== undefined ) {
ctx . globalAlpha = st [ 'marker-fill-opacity' ] || st [ 'marker-opacity' ] ;
}
2015-03-04 18:08:46 +08:00
ctx . drawImage ( img , 0 , 0 , img . width , img . height ) ;
2015-01-14 23:51:32 +08:00
}
2014-12-10 01:05:11 +08:00
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
module . exports = {
renderPoint : renderPoint ,
renderSprite : renderSprite ,
renderRectangle : renderRectangle
} ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
} , { } ] , 23 : [ function ( require , module , exports ) {
module . exports = {
cartocss : require ( './cartocss_render' ) ,
Point : require ( './point' ) ,
Rectangle : require ( './rectangle' )
} ;
} , { "./cartocss_render" : 22 , "./point" : 24 , "./rectangle" : 25 } ] , 24 : [ function ( require , module , exports ) {
( function ( global ) {
var torque = require ( '../' ) ;
var cartocss = require ( './cartocss_render' ) ;
var Profiler = require ( '../profiler' ) ;
var carto = global . carto || require ( 'carto' ) ;
2015-03-04 18:08:46 +08:00
var Filters = require ( './torque_filters' ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var TAU = Math . PI * 2 ;
var DEFAULT _CARTOCSS = [
'#layer {' ,
' marker-fill: #662506;' ,
' marker-width: 4;' ,
' [value > 1] { marker-fill: #FEE391; }' ,
' [value > 2] { marker-fill: #FEC44F; }' ,
' [value > 3] { marker-fill: #FE9929; }' ,
' [value > 4] { marker-fill: #EC7014; }' ,
' [value > 5] { marker-fill: #CC4C02; }' ,
' [value > 6] { marker-fill: #993404; }' ,
' [value > 7] { marker-fill: #662506; }' ,
'}'
] . join ( '\n' ) ;
2014-12-17 22:34:45 +08:00
var COMP _OP _TO _CANVAS = {
"src" : 'source-over' ,
"src-over" : 'source-over' ,
"dst-over" : 'destination-over' ,
"src-in" : 'source-in' ,
"dst-in" : 'destination-in' ,
"src-out" : 'source-out' ,
"dst-out" : 'destination-out' ,
"src-atop" : 'source-atop' ,
"dst-atop" : 'destination-atop' ,
"xor" : 'xor' ,
"darken" : 'darken' ,
"lighten" : 'lighten'
}
function compop2canvas ( compop ) {
return COMP _OP _TO _CANVAS [ compop ] || compop ;
}
2014-12-10 01:05:11 +08:00
//
// this renderer just render points depending of the value
//
function PointRenderer ( canvas , options ) {
if ( ! canvas ) {
throw new Error ( "canvas can't be undefined" ) ;
2014-03-21 22:57:51 +08:00
}
2014-12-10 01:05:11 +08:00
this . options = options ;
this . _canvas = canvas ;
this . _ctx = canvas . getContext ( '2d' ) ;
this . _sprites = [ ] ; // sprites per layer
this . _shader = null ;
2015-03-02 18:56:25 +08:00
this . _icons = { } ;
2015-03-04 18:08:46 +08:00
this . _iconsToLoad = 0 ;
this . _filters = new Filters ( this . _canvas , { canvasClass : options . canvasClass } ) ;
2014-12-10 01:05:11 +08:00
this . setCartoCSS ( this . options . cartocss || DEFAULT _CARTOCSS ) ;
this . TILE _SIZE = 256 ;
2015-03-02 18:56:25 +08:00
this . _style = null ;
this . _gradients = { } ;
2015-01-14 23:51:32 +08:00
this . _forcePoints = false ;
2014-12-10 01:05:11 +08:00
}
2013-08-29 21:22:57 +08:00
2015-01-14 23:51:32 +08:00
torque . extend ( PointRenderer . prototype , torque . Event , {
2013-08-29 21:22:57 +08:00
2014-12-17 22:34:45 +08:00
clearCanvas : function ( ) {
var canvas = this . _canvas ;
var color = this . _Map [ '-torque-clear-color' ]
// shortcut for the default value
if ( color === "rgba(255, 255, 255, 0)" || ! color ) {
this . _canvas . width = this . _canvas . width ;
} else {
var ctx = this . _ctx ;
ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
var compop = this . _Map [ 'comp-op' ]
ctx . globalCompositeOperation = compop2canvas ( compop ) ;
ctx . fillStyle = color ;
ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
}
} ,
2014-12-10 01:05:11 +08:00
setCanvas : function ( canvas ) {
this . _canvas = canvas ;
this . _ctx = canvas . getContext ( '2d' ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
//
// sets the cartocss style to render stuff
//
setCartoCSS : function ( cartocss ) {
// clean sprites
this . setShader ( new carto . RendererJS ( ) . render ( cartocss ) ) ;
} ,
2013-09-30 22:56:29 +08:00
2014-12-10 01:05:11 +08:00
setShader : function ( shader ) {
// clean sprites
this . _sprites = [ ] ;
this . _shader = shader ;
2014-12-17 22:34:45 +08:00
this . _Map = this . _shader . getDefault ( ) . getStyle ( { } , { zoom : 0 } ) ;
2015-03-02 18:56:25 +08:00
var img _names = this . _shader . getImageURLs ( ) ;
this . _preloadIcons ( img _names ) ;
2014-12-10 01:05:11 +08:00
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
clearSpriteCache : function ( ) {
this . _sprites = [ ] ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-17 22:34:45 +08:00
2014-12-10 01:05:11 +08:00
//
// generate sprite based on cartocss style
//
generateSprite : function ( shader , value , shaderVars ) {
2015-01-14 23:51:32 +08:00
var self = this ;
2014-12-10 01:05:11 +08:00
var prof = Profiler . metric ( 'torque.renderer.point.generateSprite' ) . start ( ) ;
var st = shader . getStyle ( {
value : value
} , shaderVars ) ;
2015-03-02 18:56:25 +08:00
if ( this . _style === null || this . _style !== st ) {
this . _style = st ;
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var pointSize = st [ 'marker-width' ] ;
if ( ! pointSize ) {
return null ;
}
2013-07-30 16:45:35 +08:00
2014-12-19 18:01:04 +08:00
if ( st [ 'marker-opacity' ] === 0 && ! st [ 'marker-line-opacity' ] ) {
2014-12-10 01:05:11 +08:00
return null ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var canvas = this . _createCanvas ( ) ;
var ctx = canvas . getContext ( '2d' ) ;
2015-03-04 18:08:46 +08:00
var markerFile = st [ "marker-file" ] || st [ "point-file" ] ;
var qualifiedUrl = markerFile && this . _qualifyURL ( markerFile ) ;
if ( qualifiedUrl && this . _iconsToLoad <= 0 ) {
var img = this . _icons [ qualifiedUrl ] ;
2015-03-04 19:13:46 +08:00
var dWidth = st [ 'marker-width' ] * 2 || img . width ;
2015-03-04 18:08:46 +08:00
var dHeight = ( st [ 'marker-height' ] || dWidth ) * ( img . width / img . height ) ;
canvas . width = ctx . width = dWidth ;
canvas . height = ctx . height = dHeight ;
ctx . scale ( dWidth / img . width , dHeight / img . height ) ;
cartocss . renderSprite ( ctx , img , st ) ;
} else {
// take into account the exterior ring to calculate the size
var canvasSize = ( st [ 'marker-line-width' ] || 0 ) + pointSize * 2 ;
var w = ctx . width = canvas . width = ctx . height = canvas . height = Math . ceil ( canvasSize ) ;
ctx . translate ( w / 2 , w / 2 ) ;
2014-12-10 01:05:11 +08:00
var mt = st [ 'marker-type' ] ;
if ( mt && mt === 'rectangle' ) {
cartocss . renderRectangle ( ctx , st ) ;
} else {
cartocss . renderPoint ( ctx , st ) ;
}
2013-08-29 21:22:57 +08:00
}
2014-12-10 01:05:11 +08:00
prof . end ( true ) ;
if ( torque . flags . sprites _to _images ) {
var i = this . _createImage ( ) ;
i . src = canvas . toDataURL ( ) ;
return i ;
2014-11-21 18:34:37 +08:00
}
2015-03-02 18:56:25 +08:00
2014-12-10 01:05:11 +08:00
return canvas ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
//
// renders all the layers (and frames for each layer) from cartocss
//
2015-03-04 18:08:46 +08:00
renderTile : function ( tile , key , callback ) {
if ( this . _iconsToLoad > 0 ) {
this . on ( 'allIconsLoaded' , function ( ) {
this . renderTile . apply ( this , [ tile , key , callback ] ) ;
} ) ;
return false ;
}
2014-12-10 01:05:11 +08:00
var prof = Profiler . metric ( 'torque.renderer.point.renderLayers' ) . start ( ) ;
var layers = this . _shader . getLayers ( ) ;
for ( var i = 0 , n = layers . length ; i < n ; ++ i ) {
var layer = layers [ i ] ;
if ( layer . name ( ) !== "Map" ) {
var sprites = this . _sprites [ i ] || ( this . _sprites [ i ] = { } ) ;
// frames for each layer
for ( var fr = 0 ; fr < layer . frames ( ) . length ; ++ fr ) {
var frame = layer . frames ( ) [ fr ] ;
var fr _sprites = sprites [ frame ] || ( sprites [ frame ] = [ ] ) ;
this . _renderTile ( tile , key - frame , frame , fr _sprites , layer ) ;
}
}
}
2015-03-02 18:56:25 +08:00
2014-12-10 01:05:11 +08:00
prof . end ( true ) ;
2015-03-04 18:08:46 +08:00
return callback && callback ( null ) ;
2014-12-10 01:05:11 +08:00
} ,
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
_createCanvas : function ( ) {
return this . options . canvasClass
? new this . options . canvasClass ( )
: document . createElement ( 'canvas' ) ;
} ,
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
_createImage : function ( ) {
return this . options . imageClass
? new this . options . imageClass ( )
: new Image ( ) ;
} ,
2015-03-04 18:08:46 +08:00
_setImageSrc : function ( img , url , callback ) {
if ( this . options . setImageSrc ) {
this . options . setImageSrc ( img , url , callback ) ;
} else {
img . onload = function ( ) {
callback ( null ) ;
} ;
img . onerror = function ( ) {
callback ( new Error ( 'Could not load image' ) ) ;
} ;
img . src = url ;
}
} ,
2015-03-02 18:56:25 +08:00
_qualifyURL : function ( url ) {
if ( typeof this . options . qualifyURL !== "undefined" ) {
return this . options . qualifyURL ( url ) ;
}
else {
var a = document . createElement ( 'a' ) ;
a . href = url ;
return a . href ;
}
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
//
// renders a tile in the canvas for key defined in
// the torque tile
//
_renderTile : function ( tile , key , frame _offset , sprites , shader , shaderVars ) {
2015-01-14 23:51:32 +08:00
if ( ! this . _canvas ) return ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
var prof = Profiler . metric ( 'torque.renderer.point.renderTile' ) . start ( ) ;
var ctx = this . _ctx ;
2014-12-17 22:34:45 +08:00
var blendMode = compop2canvas ( shader . eval ( 'comp-op' ) ) || this . options . blendmode ;
2015-01-14 23:51:32 +08:00
if ( blendMode ) {
2014-12-10 01:05:11 +08:00
ctx . globalCompositeOperation = blendMode ;
}
if ( this . options . cumulative && key > tile . maxDate ) {
//TODO: precache because this tile is not going to change
key = tile . maxDate ;
}
var tileMax = this . options . resolution * ( this . TILE _SIZE / this . options . resolution - 1 )
var activePixels = tile . timeCount [ key ] ;
2015-03-03 00:35:13 +08:00
var anchor = this . options . resolution / 2 ;
2015-01-14 23:51:32 +08:00
if ( activePixels ) {
2014-12-10 01:05:11 +08:00
var pixelIndex = tile . timeIndex [ key ] ;
for ( var p = 0 ; p < activePixels ; ++ p ) {
var posIdx = tile . renderDataPos [ pixelIndex + p ] ;
var c = tile . renderData [ pixelIndex + p ] ;
2015-01-14 23:51:32 +08:00
if ( c ) {
2014-12-10 01:05:11 +08:00
var sp = sprites [ c ] ;
2015-01-14 23:51:32 +08:00
if ( sp === undefined ) {
2014-12-10 01:05:11 +08:00
sp = sprites [ c ] = this . generateSprite ( shader , c , torque . extend ( { zoom : tile . z , 'frame-offset' : frame _offset } , shaderVars ) ) ;
}
if ( sp ) {
2015-03-03 00:35:13 +08:00
var x = tile . x [ posIdx ] - ( sp . width >> 1 ) + anchor ;
var y = tileMax - tile . y [ posIdx ] + anchor ; // flip mercator
2014-12-10 01:05:11 +08:00
ctx . drawImage ( sp , x , y - ( sp . height >> 1 ) ) ;
}
}
}
}
2015-03-02 18:56:25 +08:00
2014-12-10 01:05:11 +08:00
prof . end ( true ) ;
} ,
2013-11-07 22:54:10 +08:00
2014-12-10 01:05:11 +08:00
setBlendMode : function ( b ) {
this . options . blendmode = b ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
/ * *
* get active points for a step in active zoom
* returns a list of bounding boxes [ [ sw , ne ] , [ ] , [ ] ] where ne is a { lat : . . , lon : ... } obj
* empty list if there is no active pixels
* /
getActivePointsBBox : function ( tile , step ) {
var positions = [ ] ;
var mercator = new torque . Mercator ( ) ;
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
var tileMax = this . options . resolution * ( this . TILE _SIZE / this . options . resolution - 1 ) ;
//this.renderer.renderTile(tile, this.key, pos.x, pos.y);
var activePixels = tile . timeCount [ step ] ;
var pixelIndex = tile . timeIndex [ step ] ;
for ( var p = 0 ; p < activePixels ; ++ p ) {
var posIdx = tile . renderDataPos [ pixelIndex + p ] ;
var c = tile . renderData [ pixelIndex + p ] ;
if ( c ) {
var x = tile . x [ posIdx ] ;
var y = tileMax - tile . y [ posIdx ] ; // flip mercator
positions . push ( mercator . tilePixelBBox (
tile . coord . x ,
tile . coord . y ,
tile . coord . z ,
x , y
) ) ;
2013-11-11 23:46:42 +08:00
}
2014-12-10 01:05:11 +08:00
}
return positions ;
} ,
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
// return the value for x, y (tile coordinates)
// null for no value
getValueFor : function ( tile , step , px , py ) {
var mercator = new torque . Mercator ( ) ;
var res = this . options . resolution ;
var res2 = res >> 1 ;
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
var tileMax = this . options . resolution * ( this . TILE _SIZE / this . options . resolution - 1 ) ;
//this.renderer.renderTile(tile, this.key, pos.x, pos.y);
var activePixels = tile . timeCount [ step ] ;
var pixelIndex = tile . timeIndex [ step ] ;
for ( var p = 0 ; p < activePixels ; ++ p ) {
var posIdx = tile . renderDataPos [ pixelIndex + p ] ;
var c = tile . renderData [ pixelIndex + p ] ;
if ( c ) {
var x = tile . x [ posIdx ] ;
var y = tileMax - tile . y [ posIdx ] ;
var dx = px + res2 - x ;
var dy = py + res2 - y ;
if ( dx >= 0 && dx < res && dy >= 0 && dy < res ) {
return {
value : c ,
bbox : mercator . tilePixelBBox (
tile . coord . x ,
tile . coord . y ,
tile . coord . z ,
x - res2 , y - res2 , res
)
}
}
}
2014-09-24 19:12:44 +08:00
}
2014-12-10 01:05:11 +08:00
return null ;
2015-01-14 23:51:32 +08:00
} ,
2015-03-04 18:08:46 +08:00
_preloadIcons : function ( img _names ) {
2015-01-14 23:51:32 +08:00
var self = this ;
2015-03-04 18:08:46 +08:00
if ( img _names . length > 0 && ! this . _forcePoints ) {
var qualifiedImageUrlSet = Object . keys ( img _names . reduce ( function ( imgNamesMap , imgName ) {
var qualifiedUrl = self . _qualifyURL ( imgName ) ;
if ( ! self . _icons [ qualifiedUrl ] ) {
imgNamesMap [ qualifiedUrl ] = true ;
}
return imgNamesMap ;
} , { } ) ) ;
var filtered = self . _shader . getLayers ( ) . some ( function ( layer ) {
return typeof layer . shader [ "image-filters" ] !== "undefined" ;
} ) ;
this . _iconsToLoad += qualifiedImageUrlSet . length ;
qualifiedImageUrlSet . forEach ( function ( qualifiedImageUrl ) {
self . _icons [ qualifiedImageUrl ] = null ;
var img = self . _createImage ( ) ;
if ( filtered ) {
img . crossOrigin = 'Anonymous' ;
2015-03-02 18:56:25 +08:00
}
2015-03-04 18:08:46 +08:00
self . _setImageSrc ( img , qualifiedImageUrl , function ( err ) {
if ( err ) {
self . _forcePoints = true ;
self . clearSpriteCache ( ) ;
if ( filtered ) {
console . info ( "Only CORS-enabled, or same domain image-files can be used in combination with image-filters" ) ;
}
console . error ( "Couldn't get marker-file " + qualifiedImageUrl ) ;
} else {
self . _icons [ qualifiedImageUrl ] = img ;
self . _iconsToLoad -- ;
if ( self . _iconsToLoad <= 0 ) {
2015-03-02 18:56:25 +08:00
self . clearSpriteCache ( ) ;
self . fire ( "allIconsLoaded" ) ;
2015-01-14 23:51:32 +08:00
}
2015-03-02 18:56:25 +08:00
}
2015-03-04 18:08:46 +08:00
} ) ;
} ) ;
} else {
this . fire ( "allIconsLoaded" ) ;
2015-03-02 18:56:25 +08:00
}
} ,
2015-03-04 18:08:46 +08:00
2015-03-02 18:56:25 +08:00
applyFilters : function ( ) {
if ( this . _style ) {
if ( this . _style [ 'image-filters' ] ) {
function gradientKey ( imf ) {
var hash = ""
for ( var i = 0 ; i < imf . args . length ; i ++ ) {
var rgb = imf . args [ i ] . rgb ;
hash += rgb [ 0 ] + ":" + rgb [ 1 ] + ":" + rgb [ 2 ] ;
2015-01-14 23:51:32 +08:00
}
2015-03-02 18:56:25 +08:00
return hash ;
}
var gradient = this . _gradients [ gradientKey ( this . _style [ 'image-filters' ] ) ] ;
if ( ! gradient ) {
function componentToHex ( c ) {
var hex = c . toString ( 16 ) ;
return hex . length == 1 ? "0" + hex : hex ;
}
function rgbToHex ( r , g , b ) {
return "#" + componentToHex ( r ) + componentToHex ( g ) + componentToHex ( b ) ;
}
gradient = { } ;
var colorize = this . _style [ 'image-filters' ] . args ;
2015-01-14 23:51:32 +08:00
2015-03-02 18:56:25 +08:00
var increment = 1 / colorize . length ;
for ( var i = 0 ; i < colorize . length ; i ++ ) {
var key = increment * i + increment ;
var rgb = colorize [ i ] . rgb ;
var formattedColor = rgbToHex ( rgb [ 0 ] , rgb [ 1 ] , rgb [ 2 ] ) ;
gradient [ key ] = formattedColor ;
}
this . _gradients [ gradientKey ( this . _style [ 'image-filters' ] ) ] = gradient ;
2015-01-14 23:51:32 +08:00
}
2015-03-02 18:56:25 +08:00
this . _filters . gradient ( gradient ) ;
this . _filters . draw ( ) ;
2015-01-14 23:51:32 +08:00
}
2015-03-02 18:56:25 +08:00
}
2015-01-14 23:51:32 +08:00
}
} ) ;
2014-03-21 22:57:51 +08:00
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// exports public api
module . exports = PointRenderer ;
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
2015-03-02 18:56:25 +08:00
} , { "../" : 10 , "../profiler" : 17 , "./cartocss_render" : 22 , "./torque_filters" : 26 , "carto" : undefined } ] , 25 : [ function ( require , module , exports ) {
2014-12-10 01:05:11 +08:00
( function ( global ) {
var carto = global . carto || require ( 'carto' ) ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
var DEFAULT _CARTOCSS = [
'#layer {' ,
' polygon-fill: #FFFF00;' ,
' [value > 10] { polygon-fill: #FFFF00; }' ,
' [value > 100] { polygon-fill: #FFCC00; }' ,
' [value > 1000] { polygon-fill: #FE9929; }' ,
' [value > 10000] { polygon-fill: #FF6600; }' ,
' [value > 100000] { polygon-fill: #FF3300; }' ,
'}'
] . join ( '\n' ) ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
var TAU = Math . PI * 2 ;
2013-10-31 21:40:01 +08:00
2014-12-10 01:05:11 +08:00
//
// this renderer just render points depending of the value
//
function RectanbleRenderer ( canvas , options ) {
this . options = options ;
carto . tree . Reference . set ( torque [ 'torque-reference' ] ) ;
this . setCanvas ( canvas ) ;
this . setCartoCSS ( this . options . cartocss || DEFAULT _CARTOCSS ) ;
}
2013-10-31 21:40:01 +08:00
2014-12-10 01:05:11 +08:00
RectanbleRenderer . prototype = {
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
//
// sets the cartocss style to render stuff
//
setCartoCSS : function ( cartocss ) {
this . _cartoCssStyle = new carto . RendererJS ( ) . render ( cartocss ) ;
if ( this . _cartoCssStyle . getLayers ( ) . length > 1 ) {
throw new Error ( "only one CartoCSS layer is supported" ) ;
}
this . _shader = this . _cartoCssStyle . getLayers ( ) [ 0 ] . shader ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
setCanvas : function ( canvas ) {
if ( ! canvas ) return ;
this . _canvas = canvas ;
this . _ctx = canvas . getContext ( '2d' ) ;
} ,
2013-10-11 17:09:30 +08:00
2014-12-10 01:05:11 +08:00
accumulate : function ( tile , keys ) {
var prof = Profiler . metric ( 'RectangleRender:accumulate' ) . start ( ) ;
var x , y , posIdx , p , k , key , activePixels , pixelIndex ;
var res = this . options . resolution ;
var s = 256 / res ;
var accum = new Float32Array ( s * s ) ;
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
if ( typeof ( keys ) !== 'object' ) {
keys = [ keys ] ;
}
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
for ( k = 0 ; k < keys . length ; ++ k ) {
key = keys [ k ] ;
activePixels = tile . timeCount [ key ] ;
if ( activePixels ) {
pixelIndex = tile . timeIndex [ key ] ;
for ( p = 0 ; p < activePixels ; ++ p ) {
posIdx = tile . renderDataPos [ pixelIndex + p ] ;
x = tile . x [ posIdx ] / res ;
y = tile . y [ posIdx ] / res ;
accum [ x * s + y ] += tile . renderData [ pixelIndex + p ] ;
}
}
}
2013-10-29 01:41:27 +08:00
2014-12-10 01:05:11 +08:00
prof . end ( ) ;
return accum ;
} ,
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
renderTileAccum : function ( accum , px , py ) {
var prof = Profiler . metric ( 'RectangleRender:renderTileAccum' ) . start ( ) ;
var color , x , y , alpha ;
var res = this . options . resolution ;
var ctx = this . _ctx ;
var s = ( 256 / res ) | 0 ;
var s2 = s * s ;
var colors = this . _colors ;
if ( this . options . blendmode ) {
ctx . globalCompositeOperation = this . options . blendmode ;
}
var polygon _alpha = this . _shader [ 'polygon-opacity' ] || function ( ) { return 1.0 ; } ;
for ( var i = 0 ; i < s2 ; ++ i ) {
var xy = i ;
var value = accum [ i ] ;
if ( value ) {
x = ( xy / s ) | 0 ;
y = xy % s ;
// by-pass the style generation for improving performance
color = this . _shader [ 'polygon-fill' ] ( { value : value } , { zoom : 0 } ) ;
ctx . fillStyle = color ;
//TODO: each function should have a default value for each
//property defined in the cartocss
alpha = polygon _alpha ( { value : value } , { zoom : 0 } ) ;
if ( alpha === null ) {
alpha = 1.0 ;
}
ctx . globalAlpha = alpha ;
ctx . fillRect ( x * res , 256 - res - y * res , res , res ) ;
2014-09-24 19:12:44 +08:00
}
2014-12-10 01:05:11 +08:00
}
prof . end ( ) ;
} ,
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
//
// renders a tile in the canvas for key defined in
// the torque tile
//
2015-03-04 18:08:46 +08:00
renderTile : function ( tile , key , callback ) {
2014-12-10 01:05:11 +08:00
if ( ! this . _canvas ) return ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
var res = this . options . resolution ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
//var prof = Profiler.get('render').start();
var ctx = this . _ctx ;
var colors = this . _colors ;
var activepixels = tile . timeCount [ key ] ;
if ( activepixels ) {
var w = this . _canvas . width ;
var h = this . _canvas . height ;
//var imageData = ctx.getImageData(0, 0, w, h);
//var pixels = imageData.data;
var pixelIndex = tile . timeIndex [ key ] ;
for ( var p = 0 ; p < activePixels ; ++ p ) {
var posIdx = tile . renderDataPos [ pixelIndex + p ] ;
var c = tile . renderData [ pixelIndex + p ] ;
if ( c ) {
var color = colors [ Math . min ( c , colors . length - 1 ) ] ;
var x = tile . x [ posIdx ] ; // + px;
var y = tile . y [ posIdx ] ; //+ py;
ctx . fillStyle = color ;
ctx . fillRect ( x , y , res , res ) ;
/ *
for ( var xx = 0 ; xx < res ; ++ xx ) {
for ( var yy = 0 ; yy < res ; ++ yy ) {
var idx = 4 * ( ( x + xx ) + w * ( y + yy ) ) ;
pixels [ idx + 0 ] = color [ 0 ] ;
pixels [ idx + 1 ] = color [ 1 ] ;
pixels [ idx + 2 ] = color [ 2 ] ;
pixels [ idx + 3 ] = color [ 3 ] ;
}
}
* /
}
2014-09-24 19:12:44 +08:00
}
2014-12-10 01:05:11 +08:00
//ctx.putImageData(imageData, 0, 0);
2013-11-11 23:46:42 +08:00
}
2014-12-10 01:05:11 +08:00
//prof.end();
2015-03-04 18:08:46 +08:00
return callback && callback ( null ) ;
2013-07-30 16:45:35 +08:00
}
2014-12-10 01:05:11 +08:00
} ;
2013-07-30 16:45:35 +08:00
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
// exports public api
module . exports = RectanbleRenderer ;
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { "carto" : undefined } ] , 26 : [ function ( require , module , exports ) {
2015-03-02 18:56:25 +08:00
/ *
Based on simpleheat , a tiny JavaScript library for drawing heatmaps with Canvas ,
by Vladimir Agafonkin
https : //github.com/mourner/simpleheat
* /
'use strict' ;
2015-03-04 18:08:46 +08:00
function torque _filters ( canvas , options ) {
2015-03-02 18:56:25 +08:00
// jshint newcap: false, validthis: true
2015-03-04 18:08:46 +08:00
if ( ! ( this instanceof torque _filters ) ) { return new torque _filters ( canvas , options ) ; }
options = options || { } ;
2015-03-02 18:56:25 +08:00
this . _canvas = canvas = typeof canvas === 'string' ? document . getElementById ( canvas ) : canvas ;
this . _ctx = canvas . getContext ( '2d' ) ;
this . _width = canvas . width ;
this . _height = canvas . height ;
this . _max = 1 ;
this . _data = [ ] ;
2015-03-04 18:08:46 +08:00
this . canvasClass = options . canvasClass ;
2015-03-02 18:56:25 +08:00
}
torque _filters . prototype = {
defaultGradient : {
0.4 : 'blue' ,
0.6 : 'cyan' ,
0.7 : 'lime' ,
0.8 : 'yellow' ,
1.0 : 'red'
} ,
gradient : function ( grad ) {
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
2015-03-04 18:08:46 +08:00
var canvas = this . _createCanvas ( ) ,
2015-03-02 18:56:25 +08:00
ctx = canvas . getContext ( '2d' ) ,
gradient = ctx . createLinearGradient ( 0 , 0 , 0 , 256 ) ;
canvas . width = 1 ;
canvas . height = 256 ;
for ( var i in grad ) {
2015-03-04 18:08:46 +08:00
gradient . addColorStop ( + i , grad [ i ] ) ;
2015-03-02 18:56:25 +08:00
}
ctx . fillStyle = gradient ;
ctx . fillRect ( 0 , 0 , 1 , 256 ) ;
this . _grad = ctx . getImageData ( 0 , 0 , 1 , 256 ) . data ;
return this ;
} ,
draw : function ( ) {
if ( ! this . _grad ) {
this . gradient ( this . defaultGradient ) ;
}
var ctx = this . _ctx ;
var colored = ctx . getImageData ( 0 , 0 , this . _canvas . width , this . _canvas . height ) ;
this . _colorize ( colored . data , this . _grad ) ;
ctx . putImageData ( colored , 0 , 0 ) ;
return this ;
} ,
_colorize : function ( pixels , gradient ) {
for ( var i = 3 , len = pixels . length , j ; i < len ; i += 4 ) {
j = pixels [ i ] * 4 ; // get gradient color from opacity value
if ( j ) {
pixels [ i - 3 ] = gradient [ j ] ;
pixels [ i - 2 ] = gradient [ j + 1 ] ;
pixels [ i - 1 ] = gradient [ j + 2 ] ;
}
}
2015-03-04 18:08:46 +08:00
} ,
_createCanvas : function ( ) {
return this . canvasClass
? new this . canvasClass ( )
: document . createElement ( 'canvas' ) ;
2015-03-02 18:56:25 +08:00
}
} ;
module . exports = torque _filters ;
} , { } ] , 27 : [ function ( require , module , exports ) {
2014-12-10 01:05:11 +08:00
( function ( global ) {
var torque = require ( './core' ) ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
var lastCall = null ;
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
function jsonp ( url , callback , options ) {
options = options || { } ;
options . timeout = options . timeout === undefined ? 10000 : options . timeout ;
var head = document . getElementsByTagName ( 'head' ) [ 0 ] ;
var script = document . createElement ( 'script' ) ;
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
// function name
var fnName = options . callbackName || 'torque_' + Date . now ( ) ;
2013-12-12 21:03:35 +08:00
2014-12-10 01:05:11 +08:00
if ( torque . isFunction ( fnName ) ) {
fnName = fnName ( ) ;
}
2013-09-11 19:21:33 +08:00
2014-12-10 01:05:11 +08:00
function clean ( ) {
head . removeChild ( script ) ;
clearTimeout ( timeoutTimer ) ;
delete window [ fnName ] ;
}
2013-08-29 21:22:57 +08:00
2014-12-10 01:05:11 +08:00
window [ fnName ] = function ( ) {
clean ( ) ;
callback . apply ( window , arguments ) ;
} ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
// timeout for errors
var timeoutTimer = setTimeout ( function ( ) {
clean ( ) ;
callback . call ( window , null ) ;
} , options . timeout ) ;
2014-03-21 22:57:51 +08:00
2014-12-10 01:05:11 +08:00
// setup url
url = url . replace ( 'callback=\?' , 'callback=' + fnName ) ;
script . type = 'text/javascript' ;
script . src = url ;
script . async = true ;
// defer the loading because IE9 loads in the same frame the script
// so Loader._script is null
setTimeout ( function ( ) { head . appendChild ( script ) ; } , 0 ) ;
}
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
function get ( url , callback , options ) {
options = options || {
method : 'GET' ,
data : null ,
responseType : 'text'
} ;
lastCall = { url : url , callback : callback } ;
var request = XMLHttpRequest ;
// from d3.js
if ( global . XDomainRequest
&& ! ( "withCredentials" in request )
&& /^(http(s)?:)?\/\// . test ( url ) ) request = XDomainRequest ;
2013-11-23 01:31:04 +08:00
2014-12-10 01:05:11 +08:00
var req = new request ( ) ;
req . open ( options . method , url , true ) ;
2014-09-24 19:12:44 +08:00
2014-12-10 01:05:11 +08:00
function respond ( ) {
var status = req . status , result ;
var r = options . responseType === 'arraybuffer' ? req . response : req . responseText ;
if ( ! status && r || status >= 200 && status < 300 || status === 304 ) {
callback ( req ) ;
} else {
callback ( null ) ;
2014-09-24 19:12:44 +08:00
}
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
"onload" in req
? req . onload = req . onerror = respond
: req . onreadystatechange = function ( ) { req . readyState > 3 && respond ( ) ; } ;
req . onprogress = function ( ) { } ;
req . responseType = options . responseType ; //'arraybuffer';
if ( options . data ) {
req . setRequestHeader ( "Content-type" , "application/json" ) ;
//req.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
req . setRequestHeader ( "Accept" , "*" ) ;
}
req . send ( options . data ) ;
return req ;
2014-11-21 18:34:37 +08:00
}
2013-07-30 16:45:35 +08:00
2014-12-10 01:05:11 +08:00
function post ( url , data , callback ) {
return get ( url , callback , {
data : data ,
method : "POST"
} ) ;
}
module . exports = {
get : get ,
post : post ,
jsonp : jsonp ,
lastCall : function ( ) { return lastCall ; }
} ;
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { "./core" : 4 } ] } , { } , [ 10 ] ) ( 10 )
} ) ;