Repository: bokeh/bokeh

Issue:
Line downsample zoom to fine-level detail results in error on JS client
When you zoom in VERY far (just a few points) on a line-downsample plot, the javascript client will give the error "Uncaught TypeError: Cannot read property 'close' of undefined " in column_data_source.  The problem appears to be that the format of the return result changes when no actual downsampling is required.



Discussion:

Resolving patch:
diff --git a/bokeh/server/views/data.py b/bokeh/server/views/data.py
--- a/bokeh/server/views/data.py
+++ b/bokeh/server/views/data.py
@@ -60,8 +60,8 @@ def get_data(username, docid, datasourceid):
             plot_state,
             render_state)
 
-    result = make_json(protocol.serialize_json(result))
-    return result
+    json_result = make_json(protocol.serialize_json(result))
+    return json_result 
 
 @bokeh_app.route("/bokeh/data/upload/<username>/<name>", methods=['POST'])
 def upload(username, name):
diff --git a/bokeh/transforms/line_downsample.py b/bokeh/transforms/line_downsample.py
--- a/bokeh/transforms/line_downsample.py
+++ b/bokeh/transforms/line_downsample.py
@@ -1,10 +1,12 @@
 import numpy as np
-import math
 from ..models import ServerDataSource
 
+
 def source(domain='x', method='minmax', **kwargs):
-  kwargs['transform'] = {'resample':'line1d', 'domain':domain, 'method':method}
-  return ServerDataSource(**kwargs)
+    kwargs['transform'] = {'resample': 'line1d',
+                           'domain': domain,
+                           'method': method}
+    return ServerDataSource(**kwargs)
 
 
 def downsample(data,
@@ -27,11 +29,11 @@ def downsample(data,
     output:
     list of (domain,data) pairs, where they are each 1d vectors
     """
-    #sort data
+    # sort data
     indexes = np.argsort(data[domain_column])
     data = data[indexes]
 
-    #truncate data based on domain_limits
+    # truncate data based on domain_limits
     domain = data[domain_column]
     left_idx = np.searchsorted(domain, domain_limit[0], side='left')
     right_idx = np.searchsorted(domain, domain_limit[1], side='right')
@@ -47,42 +49,45 @@ def downsample(data,
     ending_boundaries = starting_boundaries[1:]
     ending_boundaries.append(None)
     if len(starting_boundaries) * 2 > len(data):
-        return data
+        # So little data, don't bother downsampling it
+        downsampled_data = data
+    else:
+        downsampled_data = []
+        for st, ed in zip(starting_boundaries, ending_boundaries):
+            subdata = data[st:ed]
+            if subdata.shape[0] == 0:
+                continue
 
-    downsampled_data = []
-    for st, ed in zip(starting_boundaries, ending_boundaries):
-        subdata = data[st:ed]
-        if subdata.shape[0] == 0:
-            continue
+            primary_column = subdata[primary_data_column]
+            idx = np.argsort(primary_column)
+            # downsample
+            if method == 'minmax':
+                min_idx = idx[0]
+                max_idx = idx[-1]
+                subdata = subdata[[min_idx, max_idx]]
+            elif method == 'mid':
+                mid_idx = idx[len(idx)/2]
+                subdata = subdata[[mid_idx]]
+            else:
+                raise ValueError("Line downsample method not known: " + method)
+            downsampled_data.append(subdata)
 
-        primary_column = subdata[primary_data_column]
-        idx = np.argsort(primary_column)
-        #downsample
-        if (method == 'minmax'):
-          min_idx = idx[0]
-          max_idx = idx[-1]
-          subdata = subdata[[min_idx, max_idx]]
-        elif (method == 'mid'):
-          mid_idx = idx[len(idx)/2]
-          part = subdata[[mid_idx]]
-        else :
-          raise ValueError("Line downsample method not known: " + method)
-        downsampled_data.append(subdata)
+        downsampled_data = np.concatenate(downsampled_data)
 
-    downsampled_data = np.concatenate(downsampled_data)
-    #resort data
+    # resort data
     indexes = np.argsort(downsampled_data[domain_column])
     downsampled_data = downsampled_data[indexes]
 
-    columns = dict([(k, downsampled_data[k]) for k in downsampled_data.dtype.names])
-    if  range_limit == "auto":
-      range_limit = [columns[primary_data_column].min(), columns[primary_data_column].max()]
+    columns = dict([(k, downsampled_data[k])
+                    for k in downsampled_data.dtype.names])
+    if range_limit == "auto":
+        range_limit = [columns[primary_data_column].min(),
+                       columns[primary_data_column].max()]
 
     result = {
-        'data' : columns,
-        'domain_limit' : domain_limit,
-        'range_limit' : range_limit
+        'data': columns,
+        'domain_limit': domain_limit,
+        'range_limit': range_limit
     }
 
     return result
-


Test patch: