Repository: holoviz/holoviews
Pull request #6492 (2025-01-16 22:09:52)

fix: support the case of multiple line segments each with one vdim
Previously `Path` would accept data that was structured as a list of dictionaries, each dictionary representing a single line segment. Such lists would plot correctly but if you had any `vdims` defined on the dictionary, a warning would be raised when trying to create a `ColumnDataSource` because the `_element_transform` function would make it so that the list of vdims and the list of coordinates had different lengths. This PR fixes this issue, so a list of dictionaries, each with its own coordinates and vdims, is now allowed. The below screenshot illustrates the desired plot.

![Screenshot 2025-01-16 at 4 42 01 PM](https://github.com/user-attachments/assets/42060db5-2de3-40b9-aa34-b49bce90d482)



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/holoviz/holoviews/pull/6492?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 88.76%. Comparing base [(`e1f584b`)](https://app.codecov.io/gh/holoviz/holoviews/commit/e1f584b4bf2398113c652f0c52c93660959488a9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz) to head [(`cee8069`)](https://app.codecov.io/gh/holoviz/holoviews/commit/cee8069de185f76729a1bd39f080579bb56ba118?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #6492      +/-   ##
==========================================
- Coverage   88.76%   88.76%   -0.01%     
==========================================
  Files         323      323              
  Lines       68679    68695      +16     
==========================================
+ Hits        60961    60975      +14     
- Misses       7718     7720       +2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/holoviz/holoviews/pull/6492?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz).

hoxbro: Thank you for the PR :+1: 

Patch:
diff --git a/holoviews/plotting/bokeh/path.py b/holoviews/plotting/bokeh/path.py
index 883d900d3..cebdace51 100644
--- a/holoviews/plotting/bokeh/path.py
+++ b/holoviews/plotting/bokeh/path.py
@@ -52,8 +52,17 @@ class PathPlot(LegendPlot, ColorbarPlot):
                 else:
                     new_data.append(d)
             return np.array(new_data)
-        return np.concatenate([transform.apply(el, ranges=ranges, flat=True)
-                               for el in element.split()])
+
+        transformed = []
+        for el in element.split():
+            new_el = transform.apply(el, ranges=ranges, flat=True)
+            if len(new_el) == 1:
+                kdim_length = len(el[el.kdims[0]])
+                transformed.append(np.tile(new_el, kdim_length - 1))
+            else:
+                transformed.append(new_el)
+
+        return np.concatenate(transformed)
 
     def _hover_opts(self, element):
         cdim = element.get_dimension(self.color_index)


Test patch:
diff --git a/holoviews/tests/plotting/bokeh/test_pathplot.py b/holoviews/tests/plotting/bokeh/test_pathplot.py
index 8185cdcee..a74388748 100644
--- a/holoviews/tests/plotting/bokeh/test_pathplot.py
+++ b/holoviews/tests/plotting/bokeh/test_pathplot.py
@@ -193,6 +193,27 @@ class TestPathPlot(TestBokehPlot):
         self.assertEqual(property_to_dict(item.label), legend)
         self.assertEqual(item.renderers, [plot.handles['glyph_renderer']])
 
+    def test_path_multiple_segments_with_single_vdim(self):
+
+        # two segments, each with its own color
+        data = [{
+            'x': [0, 1, 1],
+            'y': [0, 0, 1],
+            'color': '#FF0000',
+        }, {
+            'x': [0, 0, 1],
+            'y': [0, 1, 1],
+            'color': '#0000FF',
+        }]
+
+        path = Path(data, vdims='color').opts(line_color='color')
+        plot = bokeh_renderer.get_plot(path)
+        cds = plot.handles['cds']
+        source = plot.handles['source']
+        np.testing.assert_equal(source.data['xs'], [np.array([0, 1]), np.array([1, 1]), np.array([0, 0]), np.array([0, 1])])
+        np.testing.assert_equal(source.data['ys'], [np.array([0, 0]), np.array([0, 1]), np.array([0, 1]), np.array([1, 1])])
+        assert list(cds.data['line_color']) == ['#FF0000', '#FF0000', '#0000FF', '#0000FF']
+
 
 class TestPolygonPlot(TestBokehPlot):