Shallow Thoughts : : Sep
Akkana's Musings on Open Source Computing and Technology, Science, and Nature.
Sun, 27 Sep 2015
Every now and then I need to create a series of contrasting colors.
For instance, in my mapping app
PyTopo,
when displaying several track logs at once, I want them to be different
colors so it's easy to tell which track is which.
Of course, I could make a list of five or ten different colors and
cycle through the list. But I hate doing work that a computer could
do for me.
Choosing random RGB (red, green and blue) values for the colors,
though, doesn't work so well. Sometimes you end up getting two similar
colors together. Other times, you get colors that just don't work
well, because they're so light they look white, or so dark they look
black, or so unsaturated they look like shades of grey.
What does work well is converting to the HSV color space:
hue, saturation and value.
Hue is a measure of the color -- that it's red, or blue, or
yellow green, or orangeish, or a reddish purple.
Saturation measures how intense the color is: is it a bright, vivid
red or a washed-out red? Value tells you how light or dark it is: is
it so pale it's almost white, so dark it's almost black, or somewhere
in between? (A similar model, called HSL, substitutes Lightness for Value,
but is similar enough in concept.)
If you're not familiar with HSV, you can get a good feel for it by
playing with GIMP's color chooser (which pops up when you click the
black Foreground or white Background color swatch in GIMP's toolbox).
The vertical rainbow bar selects Hue. Once you have a hue, dragging
up or down in the square changes Saturation;
dragging right or left changes Value.
You can also change one at a time by dragging the H, S or V sliders at
the upper right of the dialog.
Why does this matter? Because once you've chosen a saturation and value,
or at least ensured that saturation is fairly high and value is
somewhere in the middle of its range, you can cycle through hues
and be assured that you'll get colors that are fairly different each time.
If you had a red last time, this time it'll be a green, or yellow, or
blue, depending on how much you change the hue.
How does this work programmatically?
PyTopo uses Python-GTK, so I need a function that takes a gtk.gdk.Color
and chooses a new, contrasting Color. Fortunately, gtk.gdk.Color already
has hue, saturation
and value
built in.
Color.hue
is a floating-point number between 0 and 1,
so I just have to choose how much to jump. Like this:
def contrasting_color(color):
'''Returns a gtk.gdk.Color of similar saturation and value
to the color passed in, but a contrasting hue.
gtk.gdk.Color objects have a hue between 0 and 1.
'''
if not color:
return self.first_track_color;
# How much to jump in hue:
jump = .37
return gtk.gdk.color_from_hsv(color.hue + jump,
color.saturation,
color.value)
What if you're not using Python-GTK?
No problem. The first time I used this technique, I was generating
Javascript code for a company's analytics web page.
Python's
colorsys module works fine for converting red, green, blue triples
to HSV (or a variety of other colorspaces) which you can then use in
whatever graphics package you prefer.
Tags: programming, python, gimp, pytopo
[
13:27 Sep 27, 2015
More programming |
permalink to this entry |
]
Mon, 21 Sep 2015
The street for a substantial radius around my mailbox has a wonderful,
strong minty smell.
The smell is coming from a clump of modest little yellow flowers.
They're apparently Dyssodia papposa, whose common name is "fetid marigold".
It's in the sunflower family, Asteraceae, not related to Lamiaceae, the mints.
"Fetid", of course, means "Having an offensive smell; stinking".
When I google for fetid marigold, I find quotes like
"This plant is so abundant, and exhales an odor so unpleasant as to
sicken the traveler over the western prairies of Illinois, in
autumn." And nobody says it smells like mint -- at least, googling
for the plant and "mint" or "minty" gets nothing.
But Dave and I both find the smell very minty and pleasant,
and so do most of the other local people I queried.
What's going on?
Another local plant which turns strikingly red in autumn has an even
worse name: fetid goosefoot. On a recent hike, several of us made a
point of smelling it. Sure enough: everybody except one found it
minty and pleasant. But one person on the hike said "Eeeeew!"
It's amazing how people's sensory perception can vary. Everybody knows
how people's taste varies: some people perceive broccoli and cabbage
as bitter while others love the taste. Some people can't taste lobster
and crab at all and find Parmesan cheese unpleasant.
And then there's color vision.
Every amateur astronomer who's worked public star parties knows about
Albireo. Also known as beta Cygni, Albireo is a double star, the head
of the constellation of the swan or the foot of the Northern Cross.
In a telescope, it's a double star, and a special type of double:
what's known as a "color double", two stars which are very different
colors from each other.
Most non-astronomers probably don't think of stars having colors.
Mostly, color isn't obvious when you're looking at things at night:
you're using your rods, the cells in your retina that are sensitive
to dim light, not your cones, which provide color vision but need
a fair amount of light to work right.
But when you have two things right next to each other that are
different colors, the contrast becomes more obvious. Sort of.
Point a telescope at Albireo at a public star party and ask the
next ten people what two colors they see. You'll get at least six,
more likely eight, different answers. I've heard blue and red, blue
and gold, red and gold, red and white, pink and blue ... and white
and white (some people can't see the colors at all).
Officially, the bright component is actually a close binary, too close
to resolve as separate stars. The components are
Aa (magnitude 3.18, spectral type K2II) and
Ac (magnitude 5.82, spectral type B8).
(There doesn't seem to be an Albireo Ab.)
Officially that makes Albireo A's combined color yellow or amber.
The dimmer component, Albireo B, is magnitude 5.09 and spectral
type B8Ve: officially it's blue.
But that doesn't make the rest of the observers wrong. Color vision is
a funny thing, and it's a lot more individual than most people think.
Especially in dim light, at the limits of perception.
I'm sure I'll continue to ask that question when I show Albireo
in my telescope, fascinated with the range of answers.
In case you're wondering,
I see Albireo's components as salmon-pink and pale blue.
I enjoy broccoli and lobster but find bell peppers bitter.
And I love the minty smell of plants that a few people, apparently,
find "fetid".
Tags: nature, plants, astronomy, perception
[
16:09 Sep 21, 2015
More nature |
permalink to this entry |
]
Tue, 15 Sep 2015
I wrote last week about
tweaking a
Kobo e-reader's sqlite database by hand.
But who wants to remember all the table names and type out those queries?
I sure don't. So I wrote a Python wrapper that makes it much easier to
interact with the Kobo databases.
Happily, Python already has a module called sqlite3.
So all I had to do was come up with an API that included the calls
I typically wanted -- list all the books, list all the shelves,
figure out which books are on which shelves, and so forth.
The result was
kobo_utils.py,
which includes a main function that can list books, shelves, or shelf contents.
You can initialize kobo_utils like this:
import kobo_utils
koboDB = KoboDB("/path/where/your/kobo/is/mounted")
koboDB.connect("/path/to/KoboReader.sqlite")
connect()
throws an exception if it can't find the
.sqlite file.
Then you can list books thusly:
koboDB.list_books()
or list shelf names:
koboDB.list_shelves()
or use
print_shelf
which books are on which shelves:
shelves = koboDB.get_dlist("Shelf", selectors=[ "Name" ])
for shelf in shelves:
print shelf["Name"]
What I really wanted, though, was a way to organize my library,
taking the tags in each of my epub books and assigning them to
an appropriate shelf on the Kobo, creating new shelves as needed.
Using kobo_utils.py plus the
Python
epub library I'd already written, that ended up being quite
straightforward:
shelves_by_tag.
Tags: ebook, kobo, epub, database, sql, sqlite
[
20:38 Sep 15, 2015
More tech |
permalink to this entry |
]
Thu, 10 Sep 2015
One of the adjustments we've had to make in moving to New Mexico is
getting used to the backward (compared to California) weather.
Like, rain in summer!
Not only is rain much more pleasant in summer, as a dramatic
thundershower that cools you off on a hot day instead of a constant
cold drizzle in winter (yes, I know that by now Calfornians need
a lot more of that cold drizzle! But it's still not very
pleasant being out in it). Summer rain has another unexpected effect:
flowers all summer, a constantly changing series of them.
Right now the purple asters are just starting up,
while skyrocket gilia and the last of the red penstemons add a note
of scarlet to a huge array of yellow flowers of all shapes and sizes.
Here's the vista that greeted us on a hike last weekend
on the Quemazon trail.
Down in the piñon-juniper where we live, things aren't usually
quite so colorful; we lack many red blooms, though we have just as many
purple asters as they do up on the hill, plus lots of pale trumpets
(a lovely pale violet gilia) and Cowpen daisy, a type of yellow sunflower.
But the real surprise is a plant with a modest name: snakeweed. It has
other names, but they're no better: matchbrush, broomweed. It grows
everywhere, and most of the year it just looks like a clump of bunchgrass.
Then come September, especially in a rainy year like this one,
and all that snakeweed suddenly bursts into a glorious carpet of gold.
We have plenty of other weeds -- learning how to identify Russian thistle
(tumbleweed), kochia and amaranth when they're young, so we can pull
them up before they go to seed and spread farther, has launched me on
a project of an Invasive Plants page for the nature center (we should be
ready to make that public soon).
But snakeweed, despite the name, is a welcome guest in our yard, and
it lifts my spirits to walk through it on a September evening.
By the way, if anyone in Los Alamos reads this blog, Dave and I are
giving our first planetarium show at the nature center tomorrow (that's
Friday) afternoon.
Unlike most PEEC planetarium shows, it's free! Which is probably just
as well since it's our debut. If you want to come see us, the info is here:
Night Sky Fiesta
Planetarium Show.
Tags: nature, wildflowers, astronomy, speaking
[
21:24 Sep 10, 2015
More nature |
permalink to this entry |
]
Thu, 03 Sep 2015
I've been enjoying reading my new Kobo Touch quite a lot. The screen
is crisp, clear and quite a bit whiter than my old Nook;
the form factor is great, it's reasonably responsive (though there
are a few places on the screen where I have to tap harder than other
places to get it to turn the page), and I'm happy with the choice of fonts.
But as I mentioned in my
previous Kobo article,
there were a few tweaks I wanted to make; and I was very happy with how
easy it was to tweak, compared to the Nook. Here's how.
Mount the Kobo
When you plug the Kobo in to USB, it automatically shows up as a
USB-Storage device once you tap "Connect" on the Kobo -- or as two
storage devices, if you have an SD card inserted.
Like the Nook, the Kobo's storage devices show up without partitions.
For instance, on Linux, they might be /dev/sdb and /dev/sdc, rather
than /dev/sdb1 and /dev/sdc1. That means they also don't present UUIDs
until after they're already mounted, so it's hard to make an entry for
them in /etc/fstab if you're the sort of dinosaur (like I am) who prefers
that to automounters.
Instead, you can use the entry in /dev/disk/by-id.
So fstab entries, if you're inclined to make them, might look like:
/dev/disk/by-id/usb-Kobo_eReader-3.16.0_N905K138254971:0 /kobo vfat user,noauto,exec,fmask=133,shortname=lower 0 0
/dev/disk/by-id/usb-Kobo_eReader-3.16.0_N905K138254971:1 /kobosd vfat user,noauto,exec,fmask=133,shortname=lower 0 0
One other complication, for me, was that the Kobo is one of a few
devices that don't work through my USB2 powered hub. Initially I
thought the Kobo wasn't working, until I tried a cable plugged
directly into my computer. I have no idea what controls which devices
work through the hub and which ones don't.
(The Kobo also doesn't give any indication when it's plugged in to a
wall charger, nor does
The sqlite database
Once the Kobo is mouted, ls -a
will show a directory
named .kobo. That's where all the good stuff is:
in particular, KoboReader.sqlite, the device's database,
and Kobo/Kobo eReader.conf, a human-readable configuration file.
Browse through Kobo/Kobo eReader.conf for your own amusement,
but the remainder of this article will be about KoboReader.sqlite.
I hadn't used sqlite before, and I'm certainly no SQL expert. But a
little web searching and experimentation taught me what I needed to know.
First, make a local copy of KoboReader.sqlite, so you don't risk
overwriting something important during your experimentation.
The Kobo is apparently good at regenerating data it needs, but
you might lose information on books you're reading.
To explore the database manually, run:
sqlite3 KoboReader.sqlite
Some useful queries
Here are some useful sqlite commands, which you can generalize to
whatever you want to search for on your own Kobo. Every query (not .tables)
must end with a semicolon.
Show all tables in the database:
.tables
The most important ones, at least to me, are content (all your books),
Shelf (a list of your shelves/collections), and ShelfContent
(the table that assigns books to shelves).
Show all column names in a table:
PRAGMA table_info(content);
There are a lot of columns in
content, so try
PRAGMA
table_info(content);
to see a much simpler table.
Show the names of all your shelves/collections:
SELECT Name FROM Shelf;
Show everything in a table:
SELECT * FROM Shelf;
Show all books assigned to shelves, and which shelves they're on:
SELECT ShelfName,ContentId FROM ShelfContent;
ContentId can be a URL to a sideloaded book, like
file:///mnt/sd/TheWitchesOfKarres.epub, or a UUID like
de98dbf6-e798-4de2-91fc-4be2723d952f for books from the Kobo store.
Show all books you have installed:
SELECT Title,Attribution,ContentID FROM content WHERE BookTitle is null ORDER BY Title;
One peculiarity of Kobo's database: each book has lots of entries,
apparently one for each chapter. The entries for chapters have the
chapter name as Title, and the book title as BookTitle. The entry
for the book as a whole has BookTitle empty, and the book title as Title.
For example, I have
file:///mnt/sd/earnest.epub sideloaded:
sqlite> SELECT Title,BookTitle from content WHERE ContentID LIKE "%hamlet%";
HAMLET, PRINCE OF DENMARK|Hamlet
PERSONS REPRESENTED.|Hamlet
ACT I.|Hamlet
Scene II. Elsinore. A room of state in the Castle.|Hamlet
Scene III. A room in Polonius's house.|Hamlet
Scene IV. The platform.|Hamlet
Scene V. A more remote part of the Castle.|Hamlet
Act II.|Hamlet
[ ... and so on ... ]
ACT V.|Hamlet
Scene II. A hall in the Castle.|Hamlet
Hamlet|
Each of these entries has Title set to the name of the chapter (an act
in the play) and BookTitle set to
Hamlet, except for the final
entry, which has Title set to
Hamlet and BookTitle set to nothing.
That's why you need that query
WHERE BookTitle is null if you
just want a list of your books.
Show all books by an author:
SELECT Title,Attribution,ContentID FROM content WHERE BookTitle is null
AND Attribution LIKE "%twain%" ORDER BY Title;
Attribution is where the author's name goes. LIKE %% searches
are case insensitive.
Update: how to change a field
I realized I didn't include how to change a field, and
that seems to fit better with this article rather than writing
a whole new blog post just for that.
The Kobo doesn't show series order. So to find a specific book, and
then update its title to include
the series and series number, do something like this:
SELECT Title,Attribution,ContentID,BookTitle FROM content WHERE Attribution LIKE "%Doyle%" AND Title LIKE "%Adventures%";
UPDATE content SET Title="Sherlock Stories 1: The Adventures of Sherlock Holmes" WHERE Attribution LIKE "%Doyle%" AND Title LIKE "%Adventures%";
To delete an entry -- in this case I had two copies of the same book
and needed to specify the ContentID of the older one:
DELETE from content WHERE Attribution LIKE "%Doyle%" AND ContentID="file:///mnt/sd/memoirs-holmes.epub";
Of course, it's a lot handier to have a program that knows these queries
so you don't have to type them in every time (especially since the sqlite3
app has no history or proper command-line editing).
But this has gotten long enough, so I'll write about that separately.
Tags: ebook, kobo, epub, database, sql, sqlite
[
19:11 Sep 03, 2015
More tech |
permalink to this entry |
]