Altair: merge multiple identical legends when using resolve_scale to merge color and shape properties - visualization

Following a frequent issue in Altair:
merging legends 1
merging legends 2
combining color and shape
I want to plot several point series with line plots and point marks visualized both with different colors, shapes, and stroke dashes:
This works as expected when using resolve_scale
x = np.arange(0, 5, 0.1)
mask = np.ones_like(x)
mask[::2] = 0
df = pd.DataFrame({
"x": x,
"y": np.sin(x)*mask + np.cos(x)*(1-mask),
"y2": np.sin(2*x)*mask + np.cos(2*x)*(1-mask) ,
"col": mask
})
base= alt.Chart(df).mark_line(point=True, size=1).encode(
alt.X("x:Q"),
color = alt.Color("col:N"),
shape = alt.Shape("col:N"),
strokeDash = alt.StrokeDash("col:N")
).resolve_scale(color="independent", shape="independent", strokeDash="independent")
base.encode(alt.Y("y:Q"))
But when concatenated with other charts with a different y-value multiple identical legends appear:
base.encode(alt.Y("y:Q")) | base.encode(alt.Y("y2:Q"))
I understand this is the purpose of "resolve_scale", would really appreciate a workaround.
not using the resolve_scale method or using it on the concatenated chart would get me a legend with every visualized property (color, shape, etc) set apart.

You have set the color, shape, and strokeDash to one thing: "col:N". If you want them to be independent, then define them as different things.
base= alt.Chart(df).mark_line(point=True, size=1).encode(
alt.X("x:Q"),
color = alt.Color("col:N"),
shape = alt.Shape("col:N"),
strokeDash = alt.StrokeDash("col:N")
)
h = base.encode(alt.Y("y:Q"), color=alt.value('red')) | base.encode(alt.Y("y2:Q"), color=alt.value('blue')).resolve_scale(color="independent", shape="independent", strokeDash="independent")
as for a workaround, you could go into the h.hconcat[0].encoding and h.hconcat[1].encoding and change the map to be whatever you want for vega-lite to read. At that point I'd just use a different library.
Hopefully this helps.

Related

Cannot get the color to work in a biplot from the RDA procedure in Vegan

I have run RDA from Vegan, and cannot generate four different colors in the "sites" on the plot that correspond to the Group trait which really is countries. The problem is colvec[Group] that fails to link up the four colors with the four different countries. I avoided this for the legend by just spelling out the colors for each country, and that worked (produced a legend with the correct colors for the country). When I take out the col = colvec[Group], the plot does produce the points ("sites"), but they all are black in color and do not differentiate the countries. I have been using the suggested code at:
https://fromthebottomoftheheap.net/2012/04/11/customising-vegans-ordination-plots/
Can anyone help suggest a modification that would produce the correct colors in the points (sites)?
Thanks much.
plot(R EDUNENVONLY)
scl <- 2
par(pty="s") #makes plot square
colvec <- c("dark green", "red", "purple", "blue")
Group <- ENVIR$Group
plot(REDUNENVONLY, choices = c(1, 2), type = "n", xlim = c(-1.5, 1.5))
with(ENVIR, points(REDUNENVONLY, display = "sites",
bg = colvec[Group], col = colvec[Group],
scaling = scl, pch = 21))
text(REDUNENVONLY, display="bp", scaling = scl, cex = 1,col=1) # add black arrows
with(ENVIR, legend("topright", legend = c("CCHINA", "JAPAN", "KOREA", "NCHINA"),
col = c("dark green", "red", "purple", "blue"), pch = c(16,16)))
[![Biplot produced from code above](https://i.stack.imgur.com/zTqHW.png)](https://i.stack.imgur.com/zTqHW.png)
[![Biplot without col = colvec[Group] ](https://i.stack.imgur.com/G4XjU.png)](https://i.stack.imgur.com/G4XjU.png)
I have tried various other solutions, and I know ggplot is another possibility, but this would require more learning (I am a novice at R) and surely some simply change to the present code will get this to work.

How to use geoserver SLD style to serve single channel elevation raster ("gray" channel) as Mapbox Terrain-RGB tiles

I have an elevation raster layer in my GeoServer with a single channel ("gray").
The "gray" values is elevations values (signed int16).
I have 2 clients:
The first one is using that elevation values as is.
The second one expect to get [Mapbox Terrain-RGB format][1]
I do not want to convert the "gray scale" format to Mapbox Terrain-RGB format and hold duplicate data in the GeoServer.
I was thinking to use the SLD style and elements to map the elevation value to the appropriate RGB value (with gradient interpolation between discrete values).
For example:
<ColorMap>
<ColorMapEntry color="#000000" quantity="-10000" />
<ColorMapEntry color="#FFFFFF" quantity="1667721.5" />
</ColorMap>
It turns out that the above example does not span the full range of colors but rather creates gray values only.
That is, it seems that it interpolate each color (red, green, blue) independent of each other.
Any idea how to make it interpolate values like that: #000000, #000001, #000002, ... , #0000FF, #000100, ..., #0001FF, ..., #FFFFFF?
Tx.
[1]: https://docs.mapbox.com/data/tilesets/reference/mapbox-terrain-rgb-v1/
I'm trying to do the same with no luck, and i think it can't be done... Check this example. It's a "gradient" [-10000, -5000, -1000, -500 ... 100000000000000000, 5000000000000000000, 1000000000000000000] with the Mapbox color codification. The color progression/interpolation is anything but linear, so i think it can't be emulated in an SLD.
If you have the elevation data in the format you desire then that is the easiest option: it just works. However, if you want a more customized solution, here's what I've done for a project using the Mapbox Terrain-RGB format:
I have a scale of colors from dark blue to light blue to white.
I want to be able to specify how many steps are used from light blue to white (default is 10).
This code uses GDAL Python bindings. The following code snippet was used for testing.
It just outputs the color mapping to a GeoTIFF file.
To get values between 0 and 1, simply use value *= 1/num_steps.
You can use that value in the lookup table to get an RGB value.
If you're only interested in outputting the colors, you can ignore everything involving gdal_translate. The colors will automatically be stored in a single-band GeoTIFF. If you do want to re-use those colors, note that this version ignores alpha values (if present). You can use gdal_translate to add those. That code snippet is also available at my gist here.
import numpy as np
import gdal
from osgeo import gdal, osr
def get_color_map(num_steps):
colors = np.zeros((num_steps, 3), dtype=np.uint8)
colors[:, 0] = np.linspace(0, 255, num_steps, dtype=np.uint8)
colors[:, 1] = colors[::-1, 0]
return colors
ds = gdal.Open('/Users/myusername/Desktop/raster.tif')
band = ds.GetRasterBand(1) # Assuming single band raster
arr = band.ReadAsArray()
arr = arr.astype(np.float32)
arr *= 1/num_steps # Ensure values are between 0 and 1 (or use arr -= arr.min() / (arr.max() - arr.min()) to normalize to 0 to 1)
colors = get_color_map(num_steps) # Create color lookup table
colors[0] = [0, 0, 0] # Set black for no data so it doesn't show up as gray in the final product.
# Create new GeoTIFF with colors included (transparent alpha channel if possible). If you don't care about including the colors in the GeoTIFF, skip this.
cols = ds.RasterXSize
rows = ds.RasterYSize
out_ds = gdal.GetDriverByName('GTiff').Create('/Users/myusername/Desktop/raster_color.tif', cols, rows, 4)
out_ds.SetGeoTransform(ds.GetGeoTransform())
out_ds.SetProjection(ds.GetProjection())
band = out_ds.GetRasterBand(1)
band.WriteArray(colors[arr]) # This can be removed if you don't care about including the colors in the GeoTIFF
band = out_ds.GetRasterBand(2)
band.WriteArray(colors[arr]) # This can be removed if you don't care about including the colors in the GeoTIFF
band = out_ds.GetRasterBand(3)
band.WriteArray(colors[arr]) # This can be removed if you don't care about including the colors in the GeoTIFF
band = out_ds.GetRasterBand(4)
alpha = np.zeros((rows, cols), dtype=np.uint8) # Create alpha channel to simulate transparency of no data pixels (assuming 0 is "no data" and non-zero is data). You can remove this if your elevation values are not 0.
alpha[arr == 0] = 255
band.WriteArray(alpha) # This can be removed if you don't care about including the colors in the GeoTIFF
out_ds.FlushCache()
This issue is also present in Rasterio when using a palette with multiple values. Here is an example.
However, if your raster has n-dimensions or is a masked array, the flip operation can be tricky. Here's a solution based on one of the answers in this stackoverflow question: How to vertically flip a 2D NumPy array?.

How to label ('vertically') points in graph

I would like to add labels to some points plotted using the command scatter. For the sake of simplicity, let's say I have only one point:
x = 10;
pointSize = 100;
fontSize = 20;
P = scatter(x, 0, pointSize, [0,0,0], 'filled');
text(x, 0, 'pointLabel',...
'HorizontalAlignment', 'center',...
'VerticalAlignment', 'bottom',...
'FontSize', fontSize);
The problem with the previous commands is that the text pointLabel overlaps with the point P depending on the values assigned to the properties pointsize and fontSize.
I have read the documentation of the text command, but the examples only show how to put a label horizontally aligned with a specific point in the diagram. If the alignment needs to be horizontal it is easy, but I could not find a general way to compute the y coordinate of the label pointLabel from the values of the other dimensions.
Clearly I can reach a good alignment by testing various combinations of values, but I am looking for a general solution.
Is there anyone who can help me?
This assumes you are using >=R2014b, though it can also be accomplished in older versions using set and get commands.
When a text object is created, its default units are data coordinates, but those can be changed. In your case, I'd go with points.
x = 10;
pointSize = 100;
fontSize = 20;
P = scatter(x, 0, pointSize, [0,0,0], 'filled');
t = text(x, 0, 'pointLabel',...
'HorizontalAlignment', 'center',...
'VerticalAlignment', 'bottom',...
'FontSize', fontSize);
% It's always a good idea to switch back to the default units, so remember them.
originalUnits = t.Units;
t.Units = 'points';
% Shift the text up by the sqrt(pi)/2 times the radius of the point
t.Position(2) = t.Position(2) + sqrt(pointSize)/2;
t.Units = originalUnits;
Check out Text Properties for more info. If you want to get really sophisticated, you can use the read-only property Extent and your known marker size and position to calculate when a label is overlapping one of your points. Since the default unit is in data space, no conversions are necessary.
If you're working with an older version of MATLAB, all of these options and properties are still available, you just have to work a little harder to use them. For instance, you can't direction set the position as above, but you would instead use get to assign it to a temporary variable, change it, and then use set to update. More lines of code, but ultimately the same effect.

How to format a minimalist chart with jFreeChart?

I generate a transparent chart that lets the background of a web page be seen through it.
So far I've done this (omited the populating of dataset for brevity):
lineChartObject=ChartFactory.createLineChart("Title","Legend","Amount",line_chart_dataset,PlotOrientation.VERTICAL,true,true,false);
CategoryPlot p = lineChartObject.getCategoryPlot();
Color trans = new Color(0xFF, 0xFF, 0xFF, 0);
lineChartObject.setBackgroundPaint(trans);
p.setBackgroundPaint(trans);
for (int i=0;i<=3;i++){
lineChartObject.getCategoryPlot().getRenderer().setSeriesStroke(i, new BasicStroke(3.0f));
lineChartObject.getCategoryPlot().getRenderer().setBaseItemLabelsVisible(false);
}
Which renders this:
I cannot find a way of:
Removing border of plot (1)
Removing border of leyend as well as making it transparent (3)
Making the labels on the X axis (2) to behave intelligently as the labels of Y axis do (A). Labels of Y axis space themselves so as to not clutter the graph, for example if I rendered the graph smaller, it would show fewer labels, like this:
Edit: X label domain is dates.
For (1) try:
plot.setOutlineVisible(false);
For (2), a common reason for having too many categories along the x-axis is that the data is actually numerical, in which case you should be using XYPlot rather than CategoryPlot. With XYPlot, the x-axis scale adjusts in the same way that the y-axis does.
Edit from OP: Using a TimeSeriesChart with a TimeSeriesCollection as XYDataSet did the work! (fotgot to say X domain is dates)
For (3) try:
LegendTitle legend = chart.getLegend();
legend.setFrame(BlockBorder.NONE);
legend.setBackgroundPaint(new Color(0, 0, 0, 0));

Why sometimes the bars with bar or hist have no border?

I noticed that sometimes, when I plot the bars using the functions bar() or hist(), the bars plotted have no border. They are a little bit less nice to see, and I would prefer the border or a little bit of space between them.
The figure shows what I got now, plotting three different datasets. The third graph is zoomed in order to show the lack of space between bars.
I get there this has something to do with the 'histc' parameters in bar function. Without the histc parameters the bars have some space between each others, but then the bar will be centered on the edges values, whereas I want the edges values to be, well, EDGES of each bar.
This is (the relevant part of) the code I used:
[...]
if edges==0
%these following lines are used to take the min and max of the three dataset
maxx=max(cellfun(#max, reshape(data,1,size(data,1)*size(data,2))));
minn=min(cellfun(#min, reshape(data,1,size(data,1)*size(data,2))));
edges=minn:binW:maxx+binW;
end
[...]
y{k}=histc(data{r,c}, edges);
bar(edges,y{k} , 'histc');
[...]
I think if you change the color of your bar plots you'll see that there actually is a border it just doesn't show up very well. You can also change the width of the bars so that they are more distinct.
% something to plot
data = 100*rand(1000,1);
edges = 1:100;
hData = histc(data,edges);
figure
subplot(2,1,1)
h1 = bar(edges,hData,'histc');
% change colors
set(h1,'FaceColor','m')
set(h1,'EdgeColor','b')
% Change width
subplot(2,1,2)
h1 = bar(edges,hData,0.4,'histc');
The EdgeColor and LineWidth properties of the Barseries object control the bar outlines. Try using the code and playing with the red, green, blue, and width values to get a better result.
red = 1;
green = 1;
blue = 1;
width = 3;
h = bar(edges,y{k} , 'histc');
set(h,'EdgeColor',[red green blue],'LineWidth',width);