Shallow Thoughts : : Sep

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Sun, 27 Sep 2015

Make a series of contrasting colors with Python

[PyTopo with contrasting color track logs] 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.)

[GIMP color chooser] 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: , , ,
[ 13:27 Sep 27, 2015    More programming | permalink to this entry | ]

Mon, 21 Sep 2015

The meaning of "fetid"; Albireo; and musings on variations in sensory perception

[Fetid marigold, which actually smells wonderfully minty] 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?

[Fetid goosefoot] 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.

[Albireo, from Jefffisher10 on Wikimedia Commons] 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: , , ,
[ 16:09 Sep 21, 2015    More nature | permalink to this entry | ]

Tue, 15 Sep 2015

Hacking / Customizing a Kobo Touch ebook reader: Part II, Python

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: , , , , ,
[ 20:38 Sep 15, 2015    More tech | permalink to this entry | ]

Thu, 10 Sep 2015

The blooms of summer, and weeds that aren't weeds

[Wildflowers on the Quemazon trail] 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.

[Snakeweed in bloom] 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: , , ,
[ 21:24 Sep 10, 2015    More nature | permalink to this entry | ]

Thu, 03 Sep 2015

Hacking / Customizing a Kobo Touch ebook reader: Part I, sqlite

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: , , , , ,
[ 19:11 Sep 03, 2015    More tech | permalink to this entry | ]