Repository: altair-viz/altair
Pull request #1717 (2019-10-02 21:39:19)

ENH: update properties in project shorthand
This PR aligns the possible property options in the api function `project()` with the `Projection` class from the Vega-lite schema.


Patch:
diff --git a/altair/vegalite/v3/api.py b/altair/vegalite/v3/api.py
index 1899a593..d589960f 100644
--- a/altair/vegalite/v3/api.py
+++ b/altair/vegalite/v3/api.py
@@ -177,17 +177,17 @@ class Selection(object):
         return {'selection': self.name}
 
     def __invert__(self):
-        return Selection(core.SelectionNot(**{'not': self.name}), self.selection)
+        return core.SelectionNot(**{'not': self.name})
 
     def __and__(self, other):
         if isinstance(other, Selection):
             other = other.name
-        return Selection(core.SelectionAnd(**{'and': [self.name, other]}), self.selection)      
+        return core.SelectionAnd(**{'and': [self.name, other]})
 
     def __or__(self, other):
         if isinstance(other, Selection):
             other = other.name
-        return Selection(core.SelectionOr(**{'or': [self.name, other]}), self.selection)      
+        return core.SelectionOr(**{'or': [self.name, other]})
    
     def __getattr__(self, field_name):
         return expr.core.GetAttrExpression(self.name, field_name)
@@ -564,7 +564,8 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
     def project(self, type='mercator', center=Undefined, clipAngle=Undefined, clipExtent=Undefined,
                 coefficient=Undefined, distance=Undefined, fraction=Undefined, lobes=Undefined,
                 parallel=Undefined, precision=Undefined, radius=Undefined, ratio=Undefined,
-                rotate=Undefined, spacing=Undefined, tilt=Undefined, **kwds):
+                reflectX=Undefined, reflectY=Undefined, rotate=Undefined, scale=Undefined,
+                spacing=Undefined, tilt=Undefined, translate=Undefined, **kwds):
         """Add a geographic projection to the chart.
 
         This is generally used either with ``mark_geoshape`` or with the
@@ -572,8 +573,8 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
 
         Available projection types are
         ['albers', 'albersUsa', 'azimuthalEqualArea', 'azimuthalEquidistant',
-        'conicConformal', 'conicEqualArea', 'conicEquidistant', 'equirectangular',
-        'gnomonic', 'mercator', 'orthographic', 'stereographic', 'transverseMercator']
+        'conicConformal', 'conicEqualArea', 'conicEquidistant', 'equalEarth', 'equirectangular',
+        'gnomonic', 'identity', 'mercator', 'orthographic', 'stereographic', 'transverseMercator']
 
         Attributes
         ----------
@@ -619,6 +620,10 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
 
         ratio : float
 
+        reflectX : boolean
+
+        reflectY : boolean        
+
         rotate : List(float)
             Sets the projection’s three-axis rotation to the specified angles, which must be a
             two- or three-element array of numbers [`lambda`, `phi`, `gamma`] specifying the
@@ -626,16 +631,23 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
             pitch and roll.)
 
             **Default value:** `[0, 0, 0]`
+        scale : float
+            Sets the projection's scale (zoom) value, overriding automatic fitting.            
+
         spacing : float
 
         tilt : float
 
+        translate : List(float)
+            Sets the projection's translation (pan) value, overriding automatic fitting.        
+
         """
         projection = core.Projection(center=center, clipAngle=clipAngle, clipExtent=clipExtent,
                                      coefficient=coefficient, distance=distance, fraction=fraction,
                                      lobes=lobes, parallel=parallel, precision=precision,
-                                     radius=radius, ratio=ratio, rotate=rotate, spacing=spacing,
-                                     tilt=tilt, type=type, **kwds)
+                                     radius=radius, ratio=ratio, reflectX=reflectX,
+                                     reflectY=reflectY, rotate=rotate, scale=scale, spacing=spacing,
+                                     tilt=tilt, translate=translate, type=type, **kwds)
         return self.properties(projection=projection)
 
     def _add_transform(self, *transforms):
diff --git a/doc/user_guide/interactions.rst b/doc/user_guide/interactions.rst
index a364e194..293ded86 100644
--- a/doc/user_guide/interactions.rst
+++ b/doc/user_guide/interactions.rst
@@ -251,54 +251,6 @@ over them with your mouse:
     multi_mouseover = alt.selection_multi(on='mouseover', toggle=False, empty='none')
     make_example(multi_mouseover)
 
-Composing Multiple Selections
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Altair also supports combining multiple selections using the ``&``, ``|``
-and ``~`` for respectively ``AND``, ``OR`` and ``NOT`` logical composition
-operands.
-
-In the following example there are two people who can make an interval
-selection in the chart. The person Alex makes a selection box when the
-alt-key (macOS: option-key) is selected and Morgan can make a selection
-box when the shift-key is selected.
-We use the alt.Brushconfig() to give the selection box of Morgan a different
-style.
-Now, we color the rectangles when they fall within Alex's or Morgan's
-selection.
-
-.. altair-plot::
-
-    alex = alt.selection_interval(
-        on="[mousedown[event.altKey], mouseup] > mousemove",
-        name='alex'
-    )
-    morgan = alt.selection_interval(
-        on="[mousedown[event.shiftKey], mouseup] > mousemove",
-        mark=alt.BrushConfig(fill="#fdbb84", fillOpacity=0.5, stroke="#e34a33"),
-        name='morgan'
-    )
-
-    alt.Chart(cars).mark_rect().encode(
-        x='Cylinders:O',
-        y='Origin:O',
-        color=alt.condition(alex | morgan, 'count()', alt.ColorValue("grey"))
-    ).add_selection(
-        alex, morgan
-    ).properties(
-        width=300,
-        height=180
-    )
-
-With these operators, selections can be combined in arbitrary ways:
-
-- ``~(alex & morgan)``: to select the rectangles that fall outside
-  Alex's and Morgan's selections.
-
-- ``alex | ~morgan``: to select the rectangles that fall within Alex's
-  selection or outside the selection of Morgan
-
-
 Selection Targets: Fields and Encodings
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 For any but the simplest selections, the user needs to think about exactly


Test patch:
diff --git a/altair/vegalite/v3/tests/test_api.py b/altair/vegalite/v3/tests/test_api.py
index caaa9fc8..bf8aa7f3 100644
--- a/altair/vegalite/v3/tests/test_api.py
+++ b/altair/vegalite/v3/tests/test_api.py
@@ -359,12 +359,9 @@ def test_selection():
     assert set(chart.selection.keys()) == {'selec_1', 'selec_2', 'selec_3'}
 
     # test logical operations
-    assert isinstance(single & multi, alt.Selection)
-    assert isinstance(single | multi, alt.Selection)
-    assert isinstance(~single, alt.Selection)
-    assert isinstance((single & multi)[0].group, alt.SelectionAnd)
-    assert isinstance((single | multi)[0].group, alt.SelectionOr)
-    assert isinstance((~single)[0].group, alt.SelectionNot)
+    assert isinstance(single & multi, alt.SelectionAnd)
+    assert isinstance(single | multi, alt.SelectionOr)
+    assert isinstance(~single, alt.SelectionNot)
 
     # test that default names increment (regression for #1454)
     sel1 = alt.selection_single()
@@ -478,16 +475,6 @@ def test_filter_transform_selection_predicates():
     chart = base.transform_filter(selector1 | selector2)
     assert chart.to_dict()['transform'] == [{'filter': {'selection': {'or': ['s1', 's2']}}}]
 
-    chart = base.transform_filter(selector1 | ~selector2)
-    assert chart.to_dict()['transform'] == [{'filter': {'selection': {'or': ['s1', {'not': 's2'}]}}}]
-
-    chart = base.transform_filter(~selector1 | ~selector2)
-    assert chart.to_dict()['transform'] == [{'filter': {'selection': {'or': [{'not': 's1'}, {'not': 's2'}]}}}]
-
-    chart = base.transform_filter(~(selector1 & selector2))
-    assert chart.to_dict()['transform'] == [{'filter': {'selection': {'not': {'and': ['s1', 's2']}}}}]        
-
-
 
 def test_resolve_methods():
     chart = alt.LayerChart().resolve_axis(x='shared', y='independent')