Repository: bokeh/bokeh

Issue:
GeoJSONDataSource not bringing in all attributes.
Not sure if I am missing something or if this may be expected behavior, but it would be useful to have access to other attributes for coloring patches and tooltips.

Bokeh 0.13.0
Windows 10
Chrome 68
Jupyter Lab 34.0

Here is the code to reproduce:
```python
from bokeh.io import output_notebook, show
from bokeh.models import GeoJSONDataSource
from bokeh.plotting import figure
import json

output_notebook()

data = {'type': 'FeatureCollection',
 'features': [{'type': 'Feature',
   'geometry': {'type': 'Polygon',
    'coordinates': [[[-81.08411, 32.051581999999996],
      [-81.085284, 32.048277],
      [-81.091458, 32.04988],
      [-81.090238, 32.053270999999995],
      [-81.08411, 32.051581999999996]]]},
   'properties': {'GEO_ID': '1500000US130510027003',
    'STATE': '13',
    'COUNTY': '051',
    'TRACT': '002700',
    'BLKGRP': '3',
    'NAME': '3',
    'LSAD': 'BG',
    'CENSUSAREA': 0.09}},
  {'type': 'Feature',
   'geometry': {'type': 'Polygon',
    'coordinates': [[[-81.07981699999999, 32.046715999999996],
      [-81.085284, 32.048277],
      [-81.08411, 32.051581999999996],
      [-81.07943399999999, 32.050477],
      [-81.07852799999999, 32.050323],
      [-81.07981699999999, 32.046715999999996]]]},
   'properties': {'GEO_ID': '1500000US130510027004',
    'STATE': '13',
    'COUNTY': '051',
    'TRACT': '002700',
    'BLKGRP': '4',
    'NAME': '4',
    'LSAD': 'BG',
    'CENSUSAREA': 0.083}}]}


r = json.dumps(data)
geo_source = GeoJSONDataSource(geojson=r)

TOOLTIPS = [
    ("index", "$index"),
    ("(x,y)", "($x, $y)"),
]

p = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS)
p.patches(xs="xs", ys="ys", line_width=.5,  line_color='black',color= 'orange', source=geo_source)
show(p)
```

Here is the plot:

![image](https://user-images.githubusercontent.com/1798497/44596722-530e0f80-a79b-11e8-835f-bd92b9b781dc.png)

Question:
You can see in the geojson file there are some useful points I would like to have exposed in the GeoJSONDataSource for things such as:
1.) Showing some of the 'properties' attributes in the tooltips.
     Some examples of these from the geojson shown above are:
         * 'STATE': '13',
         * 'COUNTY': '051',
         * 'TRACT': '002700',
         * 'LSAD': 'BG',
         * 'CENSUSAREA': 0.083
2.) Changing the color of a patch by some value in the 'properties' field of the geojson.

Having looked at the documentation and playing with the object in python, GeoJSONDataSource seems to not let me access the columns similar to how ColumnDataSource seems to function.

I did see here:
https://bokeh.pydata.org/en/latest/docs/user_guide/geo.html

That it says:
```
It is important to note that behind the scenes, Bokeh converts the GeoJSON coordinates into columns called x and y or xs and ys) 
```

Am I missing something, or is there a way to access the list of columns, get more of the geojson attributes to be accessible for other plot features/functions?



Discussion:
@afonit the person who added `geoJSONDataSource` a long time ago has moved on to other things. Are you interested in working on this, with some guidance? 
@bryevdv I think my skills in that regard would be sorely lacking.  However, I will take a look to see what I can see.

As a momentary stopgap - is there an alternative way in bokeh to plot coordinates into patches?  For example if one was to deconstruct the geojson into a dataframe could one load it into a ColumnDataSource and do patches that way?  Just wondering out loud.
@afonit it's certainly possible, but I am not very familiar with GeoJSON to offer any specific suggestions, and I don't have an example to point you at offhand. 
@bryevdv thanks, I will continue to play with it.
I wrote this to flatten the list into a single dataframe:
```python
completed_df = pd.concat([pd.concat([pd.DataFrame(x['geometry']),pd.DataFrame(x['properties'], index=[0])], axis=1) for x in data['features']])
```
which produces this:
![image](https://user-images.githubusercontent.com/1798497/44599010-eb5bc280-a7a2-11e8-8afd-14dc0643ed1d.png)


I can then:
```python
data = ColumnDataSource(completed_df)
```

So now I have a dataframe from the geojson, and pushed it into a ColumnDataSource.  I will just have to figure out how to address the 'coordinates' column of the ColumnDataSource and expand it into x and y's during the call to patches.
Any updates on this? I'm designing an interactive map and want the name of locations displayed in the tooltip but it is only giving the standard `"???"` when specifying the `@name` even though name is defined in my GeoJSON properties. I would appreciate some solution to this.

Also, could someone also explain to me where exactly the implementation code for GeoJSONDataSource is as well? I tried finding it to edit myself but all I found was that it was a subclass of ColumnarDataSource which is a subclass of DataSource etc. Where is the actual conversion to cords taking place?
@isaacmg if there was any update, it would be here in this issue. As is the case with most of Bokeh, the actual work is done on the JavaScript side, in BokehJS:

https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/models/sources/geojson_data_source.ts

If you're interested in working on this we are always happy to advise new contributors. 
Thanks. I'm busy the next few days but may look into it mid next week. 
Didn't find a permanent solution but did find a hackish work around based on this [blog post](http://michael-harmon.com/blog/IntroToBokeh.html). I just refactored his code a bit and found it worked well. 
```python
def convert_GeoPandas_to_Bokeh_format(gdf, geom_type):
    # Functions taken from 
    """
    Function to convert a GeoPandas GeoDataFrame to a Bokeh
    ColumnDataSource object.
    
    :param: (GeoDataFrame) gdf: GeoPandas GeoDataFrame with polygon(s) under
                                the column name 'geometry.'
                                
    :return: ColumnDataSource for Bokeh.
    """
    gdf_new = gdf.drop('geometry', axis=1).copy()
    gdf_new['x'] = gdf.apply(getGeometryCoords, 
                             geom='geometry', 
                             coord_type='x', 
                             shape_type=geo_type, 
                             axis=1)
    
    gdf_new['y'] = gdf.apply(getGeometryCoords, 
                             geom='geometry', 
                             coord_type='y', 
                             shape_type=geo_type, 
                             axis=1)
    
    return ColumnDataSource(gdf_new)


def getGeometryCoords(row, geom, coord_type, shape_type):
    """
    Returns the coordinates ('x' or 'y') of edges of a Polygon exterior.
    
    :param: (GeoPandas Series) row : The row of each of the GeoPandas DataFrame.
    :param: (str) geom : The column name.
    :param: (str) coord_type : Whether it's 'x' or 'y' coordinate.
    :param: (str) shape_type
    """
    
    # Parse the exterior of the coordinate
    #print(shape_type)
    if shape_type == 'polygon':
        exterior = row[geom].geoms[0].exterior
        if coord_type == 'x':
            # Get the x coordinates of the exterior
            return list( exterior.coords.xy[0] )    
        
        elif coord_type == 'y':
            # Get the y coordinates of the exterior
            return list( exterior.coords.xy[1] )

    elif shape_type == 'point':
        exterior = row[geom]
    
        if coord_type == 'x':
            # Get the x coordinates of the exterior
            return  exterior.coords.xy[0][0] 

        elif coord_type == 'y':
            # Get the y coordinates of the exterior
            return  exterior.coords.xy[1][0]

res = convert_GeoPandas_to_Bokeh_format(df_points, 'point')
```
Now since this was ColumnDataFrame I was able to properly see the name of my points when hovering over. 

```
s = figure(background_fill_color="lightgrey", tools=['hover', 'pan', 'wheel_zoom'])
s.patches('xs', 'ys', source=point, color="blue")
s.circle('x', 'y', source=res, color='red')

hover = s.select(dict(type=HoverTool))
hover.tooltips = [("type", "@type"), ("name", "@name")]
hover.mode = 'mouse'
```

I tested out the example and I think this issue should be closed. If you take the initial code and continue it with
```
# Set colors
data['features'][0]['properties']['color'] = 'orange'
data['features'][0]['properties']['color'] = 'blue'

r = json.dumps(data)
geo_source = GeoJSONDataSource(geojson=r)

# add some properties to tooltips
TOOLTIPS = [
    ("index", "$index"),
    ("(x,y)", "($x, $y)"),
    ('state', '@STATE'),
    ('censusarea', '@CENSUSAREA')
]

p = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS)

# use color from the data properties
p.patches(xs="xs", ys="ys", line_width=.5,  line_color='black',color='color', source=geo_source)
show(p)
```
you will end with the requested result:

![Screenshot from 2019-06-23 07-16-58](https://user-images.githubusercontent.com/18532562/59971567-1b245400-9587-11e9-9fd5-a25efc1b8497.png)


@alhoo is there something we could document better, or an example to add?


Resolving patch:
diff --git a/sphinx/source/docs/user_guide/examples/geo_geojson_source.py b/sphinx/source/docs/user_guide/examples/geo_geojson_source.py
--- a/sphinx/source/docs/user_guide/examples/geo_geojson_source.py
+++ b/sphinx/source/docs/user_guide/examples/geo_geojson_source.py
@@ -2,12 +2,21 @@
 from bokeh.models import GeoJSONDataSource
 from bokeh.plotting import figure
 from bokeh.sampledata.sample_geojson import geojson
+import json
 
 output_file("geojson.html")
 
-geo_source = GeoJSONDataSource(geojson=geojson)
+data = json.loads(geojson)
+for i in range(len(data['features'])):
+    data['features'][i]['properties']['Color'] = ['blue', 'red'][i%2]
 
-p = figure(background_fill_color="lightgrey")
-p.circle(x='x', y='y', size=15, alpha=0.7, source=geo_source)
+geo_source = GeoJSONDataSource(geojson=json.dumps(data))
+
+TOOLTIPS = [
+    ('Organisation', '@OrganisationName')
+]
+
+p = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS)
+p.circle(x='x', y='y', size=15, color='Color', alpha=0.7, source=geo_source)
 
 show(p)


Test patch: