Repository: python-visualization/folium

Issue:
ValueError raised when `key_of_x` = 0
This line:
https://github.com/python-visualization/folium/blob/f05f3ce9b3d71f87a0738f3c8c1236b933e497a9/folium/features.py#L1112
Provokes this `ValueError` to be raised, not only when `key_of_x` is `None` but also when `key_of_x == 0` (which I think is a perfectly valid value for a key):
https://github.com/python-visualization/folium/blob/f05f3ce9b3d71f87a0738f3c8c1236b933e497a9/folium/features.py#L1113
Shouldn't this line be `if key_of_x is None:`?

Discussion:

Patch:
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 9bdb40a2..72e57cb6 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -59,26 +59,32 @@ The basic workflow for contributing is:
    ```
    git checkout -b name-of-your-branch
    ```
-4. Install the dependencies listed in `requirements.txt` and `requirements-dev.txt`.
+4. Install the [miniconda](https://docs.conda.io/en/latest/miniconda.html) to avoid any external library errors.
+
+   If using `conda` one can create a development environment with:
+   ```
+   $ conda create --name FOLIUM python=3.7 --file requirements.txt --file requirements-dev.txt
+   ```
+5. Install the dependencies listed in `requirements.txt` and `requirements-dev.txt`.
    ```
    pip install -r requirements.txt
    pip install -r requirements-dev.txt
    ```
-5. Install Firefox, download [geckodriver](https://github.com/mozilla/geckodriver/releases) 
+6. Install Firefox, download [geckodriver](https://github.com/mozilla/geckodriver/releases) 
    and put it in the PATH.
-6. Make changes to your local copy of the folium repository
-7. Make sure the tests pass:
-   * in the repository folder do `pip install -e .`  (needed for notebook tests)
+7. Make changes to your local copy of the folium repository
+8. Make sure the tests pass:
+   * in the repository folder do `pip install -e . --no-deps`  (needed for notebook tests)
    * run `python -m pytest tests`
    * run `flake8 folium --max-line-length=120`
    * resolve all errors
-8. Commit those changes
+9. Commit those changes
    ```
    git add file1 file2 file3
    git commit -m 'a descriptive commit message'
    ```
-9. Push your updated branch to your fork
+10. Push your updated branch to your fork
    ```
    git push origin name-of-your-branch
    ```
-10. [Open a pull request](https://help.github.com/articles/creating-a-pull-request/) to the python-visualization/folium
+11. [Open a pull request](https://help.github.com/articles/creating-a-pull-request/) to the python-visualization/folium
diff --git a/.travis.yml b/.travis.yml
index 5515218e..069545b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,11 +25,15 @@ matrix:
     env: PY=3.7
   - name: docs
     env: PY=3.7
+  - name: "tarball"
+    env: PY=3.7
   allow_failures:
   - name: latest-branca
     env: PY=3.7
   - name: linkcheck
     env: PY=3.7
+  - name: notebooks-conding-standard
+    env: PY=3.7
 
 before_install:
   # Install miniconda and create TEST env.
@@ -54,9 +58,15 @@ before_install:
 
 # Test source distribution.
 install:
-  - python setup.py sdist && version=$(python setup.py --version) && pushd dist  && pip install --no-deps --force-reinstall folium-${version}.tar.gz && popd
+  - pip install -e . --no-deps --force-reinstall
 
 script:
+  - if [[ $TRAVIS_JOB_NAME == 'tarball' ]]; then
+      pip wheel . -w dist --no-deps ;
+      check-manifest --verbose ;
+      twine check dist/* ;
+    fi
+
   - if [[ $TRAVIS_JOB_NAME != 'docs' ]]; then
       cp -r tests/ examples/ /tmp ;
     fi
diff --git a/examples/Plugins.ipynb b/examples/Plugins.ipynb
index e683dfc2..76a74bd6 100644
--- a/examples/Plugins.ipynb
+++ b/examples/Plugins.ipynb
@@ -793,9 +793,6 @@
     "\n",
     "plugins.LocateControl().add_to(m)\n",
     "\n",
-    "# If used with Draw plugin, LocateControl needs to be added before it.\n",
-    "# Draw().add_to(m)\n",
-    "\n",
     "m"
    ]
   }
diff --git a/folium/features.py b/folium/features.py
index 94cb5206..48399899 100644
--- a/folium/features.py
+++ b/folium/features.py
@@ -1109,7 +1109,7 @@ class Choropleth(FeatureGroup):
 
             def color_scale_fun(x):
                 key_of_x = get_by_key(x, key_on)
-                if not key_of_x:
+                if key_of_x is None:
                     raise ValueError("key_on `{!r}` not found in GeoJSON.".format(key_on))
 
                 if key_of_x not in color_data.keys():
@@ -1212,7 +1212,7 @@ class DivIcon(MacroElement):
         self.options = parse_options(
             html=html,
             icon_size=icon_size,
-            icon_ancher=icon_anchor,
+            icon_anchor=icon_anchor,
             popup_anchor=popup_anchor,
             class_name=class_name,
         )
diff --git a/folium/map.py b/folium/map.py
index 1856b7bf..88b528e9 100644
--- a/folium/map.py
+++ b/folium/map.py
@@ -211,7 +211,7 @@ class Icon(MacroElement):
                      'green', 'darkgreen', 'lightgreen',
                      'blue', 'darkblue', 'cadetblue', 'lightblue',
                      'purple',  'darkpurple', 'pink',
-                     'white', 'gray', 'lightgray' 'black'}
+                     'white', 'gray', 'lightgray', 'black'}
 
     def __init__(self, color='blue', icon_color='white', icon='info-sign',
                  angle=0, prefix='glyphicon', **kwargs):
diff --git a/folium/plugins/draw.py b/folium/plugins/draw.py
index 3007ef0f..2b5e9218 100644
--- a/folium/plugins/draw.py
+++ b/folium/plugins/draw.py
@@ -44,8 +44,8 @@ class Draw(MacroElement):
         {% macro script(this, kwargs) %}
             var options = {
               position: {{ this.position|tojson }},
-              draw: {{ this.draw_options }},
-              edit: {{ this.edit_options }},
+              draw: {{ this.draw_options|tojson }},
+              edit: {{ this.edit_options|tojson }},
             }
             // FeatureGroup is to store editable layers.
             var drawnItems = new L.featureGroup().addTo(
@@ -68,6 +68,7 @@ class Draw(MacroElement):
             {{ this._parent.get_name() }}.on('draw:created', function(e) {
                 drawnItems.addLayer(e.layer);
             });
+            {% if this.export %}
             document.getElementById('export').onclick = function(e) {
                 var data = drawnItems.toGeoJSON();
                 var convertedData = 'text/json;charset=utf-8,'
@@ -79,6 +80,7 @@ class Draw(MacroElement):
                     'download', {{ this.filename|tojson }}
                 );
             }
+            {% endif %}
         {% endmacro %}
         """)
 
diff --git a/folium/plugins/locate_control.py b/folium/plugins/locate_control.py
index 9dbb9236..8bfc77d5 100644
--- a/folium/plugins/locate_control.py
+++ b/folium/plugins/locate_control.py
@@ -18,9 +18,6 @@ class LocateControl(MacroElement):
     To work properly in production, the connection needs to be encrypted, otherwise browser will not
     allow users to share their location.
 
-    WARNING: This plugin when used with Draw plugin, it must be added to your map before Draw. See
-    example below.
-
     Parameters
     ----------
     **kwargs
@@ -32,14 +29,12 @@ class LocateControl(MacroElement):
     # With default settings
     >>> LocateControl().add_to(m)
 
-    # With custom options and alongside with Draw
+    # With some custom options
     >>> LocateControl(
     ...     position="bottomright",
     ...     strings={"title": "See you current location",
     ...              "popup": "Your position"}).add_to(m))
 
-    >>> Draw(export=True).add_to(m)
-
     For more info check:
     https://github.com/domoritz/leaflet-locatecontrol
 
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 1687f6db..e4dc3d76 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,6 @@
 altair
 cartopy
+check-manifest
 descartes
 flake8
 flake8-builtins
@@ -29,5 +30,6 @@ scipy
 selenium
 sphinx
 twine
+wheel
 vega_datasets
 vincent
diff --git a/setup.cfg b/setup.cfg
index 379a84d4..1cf13d09 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -23,3 +23,12 @@ license_file = LICENSE.txt
 
 [bdist_wheel]
 universal = 1
+
+[check-manifest]
+ignore = 
+    *.yml
+    docs
+    docs/*
+    *.enc
+    tests
+    tests/*


Test patch:
diff --git a/tests/geo_grid.json b/tests/geo_grid.json
new file mode 100644
index 00000000..6ecb8905
--- /dev/null
+++ b/tests/geo_grid.json
@@ -0,0 +1,8 @@
+{"type": "FeatureCollection", "features": [
+{"id": "0", "type": "Feature", "properties": {"idx": 0}, "geometry": {"type": "Polygon", "coordinates": [[[479978.6866107661, 6676603.690234357], [491978.6866107661, 6676603.690234357], [491978.6866107661, 6664603.690234357], [479978.6866107661, 6664603.690234357], [479978.6866107661, 6676603.690234357]]]}}, 
+{"id": "1", "type": "Feature", "properties": {"idx": 1}, "geometry": {"type": "Polygon", "coordinates": [[[479978.6866107661, 6664603.690234357], [491978.6866107661, 6664603.690234357], [491978.6866107661, 6652603.690234357], [479978.6866107661, 6652603.690234357], [479978.6866107661, 6664603.690234357]]]}}, 
+{"id": "2", "type": "Feature", "properties": {"idx": 2}, "geometry": {"type": "Polygon", "coordinates": [[[479978.6866107661, 6652603.690234357], [491978.6866107661, 6652603.690234357], [491978.6866107661, 6640603.690234357], [479978.6866107661, 6640603.690234357], [479978.6866107661, 6652603.690234357]]]}}, 
+{"id": "3", "type": "Feature", "properties": {"idx": 3}, "geometry": {"type": "Polygon", "coordinates": [[[491978.6866107661, 6676603.690234357], [503978.6866107661, 6676603.690234357], [503978.6866107661, 6664603.690234357], [491978.6866107661, 6664603.690234357], [491978.6866107661, 6676603.690234357]]]}}, 
+{"id": "4", "type": "Feature", "properties": {"idx": 4}, "geometry": {"type": "Polygon", "coordinates": [[[491978.6866107661, 6664603.690234357], [503978.6866107661, 6664603.690234357], [503978.6866107661, 6652603.690234357], [491978.6866107661, 6652603.690234357], [491978.6866107661, 6664603.690234357]]]}}, 
+{"id": "5", "type": "Feature", "properties": {"idx": 5}, "geometry": {"type": "Polygon", "coordinates": [[[491978.6866107661, 6652603.690234357], [503978.6866107661, 6652603.690234357], [503978.6866107661, 6640603.690234357], [491978.6866107661, 6640603.690234357], [491978.6866107661, 6652603.690234357]]]}}
+]}
\ No newline at end of file
diff --git a/tests/test_folium.py b/tests/test_folium.py
index baab9b04..26a81819 100644
--- a/tests/test_folium.py
+++ b/tests/test_folium.py
@@ -230,6 +230,26 @@ class TestFolium(object):
         assert '"fillColor":"a_random_color","fillOpacity":0.123454321' in out_str
         assert '"fillOpacity":0.543212345' in out_str
 
+    def test_choropleth_key_on(self):
+        """Test to make sure that Choropleth function doesn't raises
+        a ValueError when the 'key_on' field is set to a column that might
+        have 0 as a value.
+        """
+        with open(os.path.join(rootpath, 'geo_grid.json')) as f:
+            geo_data = json.load(f)
+        data = pd.DataFrame({'idx': {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5},
+                             'value': {'0': 78.0, '1': 39.0, '2': 0.0, '3': 81.0, '4': 42.0, '5': 68.0}})
+        fill_color = 'BuPu'
+        columns = ['idx', 'value']
+        key_on = 'feature.properties.idx'
+
+        Choropleth(
+            geo_data=geo_data,
+            data=data,
+            key_on=key_on,
+            fill_color=fill_color,
+            columns=columns)
+
     def test_choropleth_warning(self):
         """Test that the Map.choropleth method works and raises a warning."""
         self.setup()
diff --git a/tests/test_map.py b/tests/test_map.py
index c4a6d917..042f68ca 100644
--- a/tests/test_map.py
+++ b/tests/test_map.py
@@ -92,6 +92,7 @@ def test_popup_show():
 
 
 def test_icon_valid_marker_colors():
+    assert len(Icon.color_options) == 19
     with pytest.warns(None) as record:
         for color in Icon.color_options:
             Icon(color=color)
diff --git a/tests/test_repr.py b/tests/test_repr.py
index 51f4eb2f..ed58b78e 100644
--- a/tests/test_repr.py
+++ b/tests/test_repr.py
@@ -48,11 +48,13 @@ def test__repr_png_no_image(m):
     assert png is None
 
 
+@pytest.mark.xfail
 def test__repr_png_is_bytes(m_png):
     png = m_png._repr_png_()
     assert isinstance(png, bytes)
 
 
+@pytest.mark.xfail
 @pytest.mark.skipif(sys.version_info < (3, 0),
                     reason="Doesn't work on Python 2.7.")
 def test_valid_png(m_png):