2D Range Selection Box in Chaco (Enthought) - enthought

I am trying to draw a 2D selection box on an image plot and get out the selected region. I cannot find the appropriate tool to do this.
I assumed RangeSelection2D would be appropriate for this but it seems to only actually select 1 of the 2 axes.
I could modify the BetterSelectingZoom where the box mode is similar to what I want. Does anyone know the best way or chaco tool to do this?
import enthought.traits.api as traits
import enthought.chaco.api as chaco
import enthought.chaco.tools.api as ctools
import enthought.traits.ui.api as ui
from enthought.enable.component_editor import ComponentEditor
class ImageViewer(traits.HasTraits):
plot = traits.Instance(chaco.Plot)
view = ui.View(
ui.Item('plot', editor=ComponentEditor(), show_label=False),
width=600, height=600, resizable=True)
def __init__(self, image_array, *args, **kwargs):
super(ImageViewer, self).__init__(*args, **kwargs)
pd = chaco.ArrayPlotData(imagedata=image_array)
self.plot = chaco.Plot(pd, default_origin='top left')
img_plot = self.plot.img_plot("imagedata")[0]
range_select = ctools.RangeSelection2D(component=img_plot)
range_select.left_button_selects = True
img_plot.tools.append(range_select)
range_overlay = ctools.RangeSelectionOverlay(component=img_plot)
img_plot.overlays.append(range_overlay)
if __name__ == "__main__":
import numpy as np
image = np.random.random_integers(0, 255, size=(20, 20))
imageui = ImageViewer(image)
imageui.configure_traits()

Unfortunately, there's nothing in Chaco, currently, that does what you need. That said, there's an open pull request that adds that functionality:
https://github.com/enthought/chaco/pull/162.
As that PR suggests, there's a bit of design discussion about Chaco tools, in general, that needs to happen before more tools are added.
I hope that helps,
-Tony

Related

Dynamically change chart properties with slider in altair?

I'm trying to make a histogram in Altair with a slider that dynamically sets the maxbins parameter in alt.Bin(). The idea is to show how bin choice can affect a histogram.
A working example of the histogram without dynamic bins is:
import pandas as pd
import numpy as np
import altair as alt
weather = pd.read_csv('https://raw.githubusercontent.com/altair-viz/vega_datasets/master/vega_datasets/_data/seattle-weather.csv')
alt.Chart(weather).mark_bar().encode(
alt.X("wind:Q",
bin=alt.Bin(maxbins=50)),
y='count()',
)
What I would like to do is something along these lines (this code produces an error saying 'maxbins.maxbins' is not of type 'number'):
import pandas as pd
import numpy as np
import altair as alt
weather = pd.read_csv('https://raw.githubusercontent.com/altair-viz/vega_datasets/master/vega_datasets/_data/seattle-weather.csv')
slider = alt.binding_range(min=0,
max=len(np.unique(weather.wind)),
step=1)
selector = alt.selection_single(name='maxbins',
fields=['wind'],
bind=slider,
init={'wind': len(np.unique(weather.wind))/5})
alt.Chart(weather).mark_bar().encode(
alt.X("wind:Q",
bin=alt.Bin(maxbins=selector.maxbins)),
y='count()',
).add_selection(
selector
)
I think that, more generally, my question is whether chart properties (rather than selections of data) can be set by a slider in Altair.

how to add a vector layer on top of plotly choropleth_mapbox with raster image?

I am trying to add a vector layer (geopandas) on top of choropleth_mapbox. I can easily make a new choropleth_mapbox figure as follows.
import geopandas as gpd
import requests
import plotly_express as px
from shapely.geometry import Point
# import data
data = json.loads(requests.get("http://drive.google.com/uc?id=1ex0kKZgEuhF6-92HyKLW8J1U0r_gxmDM").json())
gdf = gpd.GeoDataFrame.from_features(data["features"]).set_geometry("geometry")
# create new choropleth_mapbox
fig=px.choropleth_mapbox( gdf,
geojson=gdf.geometry,
locations=gdf.index,
color="dummy",
center={"lon": gdf.iat[0,0].centroid.x, "lat":gdf.iat[0,0].centroid.y},
mapbox_style="carto-darkmatter",
color_continuous_scale=blues,
zoom=13.5
)
and fig.show() displays the figure exactly as expected.
But if I create a figure first:
# create figure first
dummy_df = gpd.GeoDataFrame({"geometry":[Point(7.9797197,47.32547143)], "dummy":[1]})
fig=px.choropleth_mapbox( dummy_df,
geojson=dummy_df.geometry,
locations=dummy_df.index,
color="dummy",
center={"lon": gdf.iat[0,0].centroid.x, "lat":gdf.iat[0,0].centroid.y},
mapbox_style="carto-darkmatter",
color_continuous_scale=blues,
zoom=13.5
)
fig.show()
and then add the layer as follows:
fig.add_choroplethmapbox(geojson=gdf.geometry, locations=gdf.index)
I get a TypeError “object of type polygon is not JSON serialisable”
What am I doing wrong?
I have also tried add_trace(go.Choropleth(...)
but this shows empty.
What am I doing wrong? What other way to show a vector map on top of an image on top of a base layer with plotly?

CRS for HS2 snakegrid plotting in wrong location on Folium and Leaflet

I've been trying to plot some geographical date which is the HS2 snakegrid co-ordinate system
According to ESPG HS2 ESPG Geodetic Parameters Page I should be using CRS 9300 (although have also tried 9306)
When used, the location is not plotting correctly, example below uses folium to generate a basemap in WGS 84 and geopandas to add a point with the CRS changed to the HS2 9300.
The co-ordinates used should place the point just outside Euston Station but instead is plotting in Hyde Park
I've used Jupyter notebooks to run the below code, although i've also run in geodjango so i could use leaflet with proj4js string for HS2 and same thing
import folium
from folium import plugins
import geopandas as gpd
from shapely.geometry import Point
f = folium.Figure()
m = folium.Map(
location=[51.5091, -0.16006],
max_zoom = 22,
zoom_start = 16,
tiles = None,
control_scale = True,
prefer_canvas = True,
crs = 'EPSG3857',
)
folium.TileLayer('cartodbpositron', max_zoom = 22, detect_retina = True, name = 'Carto',).add_to(m)
s = gpd.GeoSeries([Point(292001.0182, 288039.0657),],)
s = s.set_crs('epsg:9300')
s.explore(name = "S", m = m, highlight = True, show = False, style_kwds={'color': 'green','fill': False},)
example output
UPDATE
I need to use HS2TN15_NTv2.gsb transformation file within pyproj
Reading the pyproj docs i should use pyproj.datadir.get_data_dir() to identify the data directory which gives \Lib\site-packages\pyproj\proj_dir\share\proj
However when i then run TransformerGroup("epsg:4258","epsg:9300") I still get the following warning
\lib\site-packages\pyproj\transformer.py:184: UserWarning: Best
transformation is not available due to missing
Grid(short_name=HS2TN15_NTv2.gsb, full_name=, package_name=, url=,
direct_download=False, open_license=False, available=False)
super().init(

How to change color in networkx graph plotted with holoviews/bokeh?

How can I change the color of individual nodes in the following example?
%pylab inline
import pandas as pd
import networkx as nx
import holoviews as hv
hv.extension('bokeh')
G = nx.Graph()
ndxs = [1,2,3,4]
G.add_nodes_from(ndxs)
G.add_weighted_edges_from([(1,2,0), (1,3,1), (1,4,-1),
(2,4,1), (2,3,-1), (3,4,10)])
hv.extension('bokeh')
%opts Graph [width=400 height=400]
padding = dict(x=(-1.1, 1.1), y=(-1.1, 1.1))
hv.Graph.from_networkx(G, nx.layout.spring_layout).redim.range(**padding)
The graph as you currently define it does not define any attributes but you could still color by the node index. To color by a particular node attribute you can use the color_index option along with a cmap. Here's how we would color by the 'index'
graph = hv.Graph.from_networkx(G, nx.layout.spring_layout)
graph.options(color_index='index', cmap='Category10').redim.range(**padding)
If you do have attributes defined on the nodes the next version of HoloViews (1.10.5) due to be released this week will be able to extract them automatically and let you use the same approach to color by those variables.
If you want to manually add node attributes until the next release you can pass in a Dataset with a single key dimension defining the node indices and any attributes you want to add defined as value dimensions, e.g.:
nodes = hv.Dataset([(1, 'A'), (2, 'B'), (3, 'A'), (4, 'B')], 'index', 'some_attribute')
hv.Graph.from_networkx(G, nx.layout.spring_layout, nodes=nodes).options(color_index='some_attribute', cmap='Category10')
Thanks to Philippjfr, here is a nice solution (using the current development version of holoviews) that uses node attributes for coloring:
%pylab inline
import pandas as pd
import networkx as nx
import holoviews as hv
hv.extension('bokeh')
G = nx.Graph()
ndxs = [1,2,3,4]
G.add_nodes_from(ndxs)
G.add_weighted_edges_from([(1,2,0), (1,3,1), (1,4,-1),
(2,4,1), (2,3,-1), (3,4,10)])
attributes = {ndx: ndx%2 for ndx in ndxs}
nx.set_node_attributes(G, attributes, 'some_attribute')
%opts Graph [width=400 height=400]
padding = dict(x=(-1.1, 1.1), y=(-1.1, 1.1))
hv.Graph.from_networkx(G, nx.layout.spring_layout)\
.redim.range(**padding)\
.options(color_index='some_attribute', cmap='Category10')

quickest way to get started with cairo

I have taken passing shots at learning Cairo in the past, but always moved on in favor of some other graphics library. My problem is that I can't find a good tutorial that gives me a simple display for my surface. I have always ended up digging through GTK or QT documentation about things that have nothing to do with what I want to do. I want to learn Cairo, not a massive OO architecture.
What is a bare bones wrapper to give me a cross-platform window with a Cairo canvas to draw on?
I have used cairo for virtually anything involving drawing. I work at a medical software company, so I prototype scientific data visualization and other things.
I have usually three ways to display my drawings:
A GTK drawing area created with a Python script and GTK;
A PNG image displayed directly on screen using Python Image Library show() method;
A PNG image saved to disk, also via Python Image Library.
A simple script derived from cairographics examples, which actually I use as a template for any new project, is:
import gtk
class Canvas(gtk.DrawingArea):
def __init__(self):
super(Canvas, self).__init__()
self.connect("expose_event", self.expose)
self.set_size_request(800,500)
def expose(self, widget, event):
cr = widget.window.cairo_create()
rect = self.get_allocation()
# you can use w and h to calculate relative positions which
# also change dynamically if window gets resized
w = rect.width
h = rect.height
# here is the part where you actually draw
cr.move_to(0,0)
cr.line_to(w/2, h/2)
cr.stroke()
window = gtk.Window()
canvas = Canvas()
window.add(canvas)
window.set_position(gtk.WIN_POS_CENTER)
window.show_all()
gtk.main()
Or if you prefer not to deal with GUI toolkits, you can create and display an image on screen, and optionally save it to file:
import cairo, Image
width = 800
height = 600
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
cr = cairo.Context(surface)
# optional conversion from screen to cartesian coordinates:
cr.translate(0, height)
cr.scale(1, -1)
# something very similar to Japanese flag:
cr.set_source_rgb(1,1,1)
cr.rectangle(0, 0, width, height)
cr.fill()
cr.arc(width/2, height/2, 150, 0, 6.28)
cr.set_source_rgb(1,0,0)
cr.fill()
im = Image.frombuffer("RGBA",
(width, height),
surface.get_data(),
"raw",
"BGRA",
0,1) # don't ask me what these are!
im.show()
# im.save('filename', 'png')
An answer to a related question demonstrates a very simple setup in Gtk2HS to draw on a drawingArea with Cairo.
import Graphics.UI.Gtk
import Graphics.Rendering.Cairo
main :: IO ()
main = do
initGUI
window <- windowNew
drawingArea <- drawingAreaNew
containerAdd window drawingArea
drawingArea `onExpose` (\_ -> renderScene drawingArea)
window `onDestroy` mainQuit
windowSetDefaultSize window 640 480
widgetShowAll window
mainGUI
renderScene :: DrawingArea -> IO Bool
renderScene da = do
dw <- widgetGetDrawWindow da
renderWithDrawable dw $ do setSourceRGBA 0.5 0.5 0.5 1.0
moveTo 100.0 100.0
showText "HelloWorld"
return True
Simply pass your Cairo animation routine to renderWithDrawable dw in renderScene.