niedziela, 6 października 2013

SimpleCart.js

Koledze założyłem sklep--nie sklep (katalog książek z możliwością złożenie zamówienia.) Koszyk obsługuje aplikacja w JavaScript pn. SimpleCart.js. Na początek powinno koledze GB wystarczyć (albo i nie....)

Nie działa toto z Internet Explorerem wersja 7 ale na szczęście mało kto dziś używa IE 7. Zrobiłem upgrade swojego (a raczej Jaśka bo ja nie korzystam z Windows) do IE 8 i zaczęło działać. Jeszcze się temu przyjrzę...

poniedziałek, 23 września 2013

Pobranie zawartości zbioru/grupy zdjęć z flickr.com

Zbiór to set a grupa to pool. Zbiór zawiera zdjęcia jednego użytkownika a grupa (pool) to zbiór zdjęć różnych użytkowników. To tak tytułem wyjaśnienia.

Flickr API zawiera funkcje flickr.groups.pools.getPhotos oraz flickr.photosets.getPhotos, które zwracają informacje o zdjęciach należących do określonego zbioru (albo grupy). Wynik wygląda jakoś tak:


<photoset id="4" primary="2483" page="1" perpage="500" pages="1" total="2">
<photo id="2484" secret="123456" server="1" title="my photo" isprimary="0" />
<photo id="2483" secret="123456" server="1" title="flickr rocks" isprimary="1" />
</photoset>

Poniższy skrypt wykorzystujący flickr.groups.pools.getPhotos/flickr.photosets.getPhotos ściąga informacje o zdjęciach dodanych do grupy/zbioru. Wynik zapisuje w Perlowej notacji:


@photos=( {'id'=>"2484", 'secret'=>"123456", 'server'=>"1", 'title'=>"my photo", 'isprimary'=>"0"},
{'id' =>"2483", 'secret'=>"123456", 'server'=>"1", 'title'=>"flickr rocks", 'isprimary'=>"1", );

Jest to zatem gotowy do dołączenia do innego skryptu np. za pomocą require kod Perla (lista haszy). Ściągana jest informacja o grupie lub zbiorze określonej/określonym przez podanie stosownego id:


flickr_albums.getPhotos.pl -s set-id
# albo
flickr_albums.getPhotos.pl -p group-id

Uwaga: moduł login2flickr.rc deklaruje m.in stosowną wartość api_key co jest niezbędne do korzystania z API flickra.


#!/usr/bin/perl
#
use Flickr::API;
use Compress::Zlib;
use Getopt::Long;
use XML::DOM;

require 'login2flickr.rc'; # prywatny moduł do

$set_id = $pool_id = $method = $Photos='';

GetOptions( "set=s" => \$set_id, "pool=s" => \$pool_id, ) ;

if ( $set_id ne "" ) {
$root_ele = 'photoset';
$method = 'flickr.photosets.getPhotos';
$ofname = "set-${set_id}.ph";
}
elsif ( $pool_id ne "" ) {
$root_ele='photos';
$method = 'flickr.groups.pools.getPhotos';
$ofname = "pool-${pool_id}.ph";
} else {
die "*** Podaj id zbioru/pula (set-id/pool-id)\n";
}

## ### ### ### ### ###
my $extras = 'date_taken,views,url_o,url_m,geo,tags';

my $api = new Flickr::API({'key' => $api_key, secret => $sharedsecret});
my $parser = XML::DOM::Parser->new();

my $nbrPages = $photoIdx = 0;

do
{
my $params = { per_page => 500, page => $nbrPages+1, extras => $extras };

$params->{group_id} = $pool_id if $pool_id;
$params->{photoset_id} = $set_id if $set_id;

##print STDERR "*** Method/params: $method, $params ***\n";
my $response = $api->execute_method($method, $params );

die "Problem: $response->{error_message}\n" if !$response->{success};

## sprawdz czy _content nie jest gzipniety, jezeli to rozpakuj:
my $content_encoding = $response->{_headers}->{'content-encoding'} ;
my $plain_content ;

if ($content_encoding =~ /gzip/ ) {
$plain_content = Compress::Zlib::memGunzip( $response->{_content});
} else { $plain_content = $response->{_content}; }

my $log = $parser->parse($plain_content);

## Dane są podzielone na strony o maksymalnej wielkości 500 zdjęć:
for $ps ( $log->getElementsByTagName( $root_ele ) ) {
if ($ps->getAttributeNode ("page") ) { $page = $ps->getAttributeNode ("page")->getValue(); }
if ($ps->getAttributeNode ("pages") ) { $pages = $ps->getAttributeNode ("pages")->getValue(); }
}

print "*** Page $page of $pages ***\n";

## dla każdego elementu:
for $p_ ( $log->getElementsByTagName('photo') ) {
$element_content='';

## zapisz wszystkie atrybuty
if ($p_ ->getAttributes() !=null){
$numberAttributes = $p_->getAttributes()->getLength();
}

for ($loopIndex =0; $loopIndex < $numberAttributes; $loopIndex++) {
$attribute = ($p_ -> getAttributes())->item($loopIndex);
$attrname = $attribute->getNodeName();
$attrvalue = $attribute->getNodeValue();
$element_content .= "'$attrname' => '$attrvalue', ";
}

$Photos .= "{ $element_content },\n";
$photoIdx++;
}

++$nbrPages;

} while ($page < $pages );

## ## ###
print STDERR "Writing to $ofname\n";
open (OFILE, ">$ofname");
print OFILE "## Created with $method\n\@photos = (\n $Photos \n);\n1;\n";
close OFILE;

print STDERR "*** $photoIdx photos written to $ofname ***\n";

Skrypt jest do pobrania tutaj.

sobota, 31 sierpnia 2013

Importowanie i wyświetlanie plików KML w Google Maps

Ubocznym skutkiem dodania współrzędnych geograficznych do zdjęć za pomocą skryptu gpsPhoto.pl jest plik w formacie KML. Każde zdjęcie w takim pliku jest oznakowane jak na przykładzie:


#1: Import do GMaps

#2: Z pinkaccordions

#3: Z sites.google.com

#4: Z sites.google.com

#5: Z serwera chello.pl

<Placemark>
<name>s300013_08275298.jpg</name>
<description><![CDATA[<a href="/home/tomek/Obrazy/s300013_08275298.jpg">
<img src="/home/tomek/Obrazy/s300013_08275298.jpg" width="200" /></a><br>
<a href="/home/tomek/Obrazy/s300013_08275298.jpg">full size</a>]]>
</description>
<Point>
<altitudeMode>clampToGround</altitudeMode>
<coordinates>16.496808361,53.466461571,122</coordinates>
</Point>

Dawno temu zrobiłem skrypt, który podmienia adres w lokalnym systemie plików (taki jak /home/tomek/Obrazy/s300013_08275298.jpg) na tenże plik, ale umieszczony na moim koncie w serwisie flickr.com:


<Placemark>
<name>s300013_08275298.jpg</name>
<description><![CDATA[<a href="http://www.flickr.com/tprzechlewski/9615076609/">
<img src="http://static.flickr.com/7411/9615076609_a243eed0f1_m.jpg" width="200" /></a><br>
<a href="http://www.flickr.com/tprzechlewski/9615076609/">full size</a>]]>
</description>
... itd ...

Taki plik za pomocą kilku klików importowałem do mapy Google (Mapy→Moje Miejsca → utwórz w klasycznej wersji Moich map) i działało -- do zeszłego piątku, kiedy to plik KML z wycieczki do Nadarzyc uparcie nie chciał się poprawnie wyświetlić (#1 -- obcięty ślad w okolicach Nadarzyc).

Konsultując problem via Google dowiedziałem się, że cyt KML upload and rendering has always been problematic with Maps i że lepiej (jeżeli już) to nie importować KML tylko wyświetlić go podając URL do dokumentu KML jako wartość parametru q. Przykład:


https://mapy.google.pl/maps?q=http://pinkaccordions.homelinux.org/Geo/kml/20130828__MP.kml

Działa (rys. #2). Ponieważ serwer pinkaccordions.homelinux.org jest rachityczny pomyślałem, że dobrze by było plik KML umieścić w jakimś lepszym miejscu, np. na witrynie sites.google.com. Jest nawet dedykowany -- i niestary bo z 2010 roku -- dokument w temacie pn. Using Google Sites to Host Your KML

Niestety plik KML pobierany z http://sites.google.com/site/ nie wyświetla się poprawnie. Po kliknięciu w ikonę niektórych zdjęć otwiera się okno ale jest ono puste (#3) i/lub nie wskazuje na stosowne miejsce na mapie (por. #4). Kombinowałem jak toto poprawić ale nie doszedłem do żadnych pozytywnych wniosków: połączenie GMaps + KML + sites.google.com nie działa.

PS: przypomniałem sobie, że mój Internet Provider daje w pakiecie konto na serwerze WWW z limitem 200Mb (z którego do tej pory mało korzystałem)... Sprawdziłem -- działa. Będę zatem tam trzymał pliki KML/KMZ (Rys. #5.)

Wycieczka do Bornego Sulinowa

We wtorek pojechaliśmy do Nadarzyc koło Bornego Sulinowa (Gross Born). Głównym punktem wycieczki był spływ rzeką Piławą. Byliśmy na miejscu (agroturystyka U Stasia w Nadarzycach nad brzegiem Piławy) tuż po 11:00. Dalej standard: pojechaliśmy samochodem właściciela i jego kajakami do mostu nad Piławą we wsi Liszkowo. Około 12:00 z tego miejsca zaczęliśmy spływ, który skończyliśmy U Stasia tuż przed 18:00.

Miejscami jest bardzo szeroko, miejscami rzeczka jest płytka i wąska. Jest odcinek z przeszkodami (zwalone drzewa), ale da się płynąć. Dwa razy trzeba przenieść kajak.

Drugi raz w życiu widziałem zimorodka!

Rzeczka jest malownicza a miejsce wydaje się odludne (byliśmy wszakże ,,po sezonie''). Płynąc Piławą warto zapoznać się z trasą, mieć GPSa albo mapę bo na jeziorach są czasami wątpliwości ,,którędy dalej...''

Następnego dnia poszwendaliśmy się po Bornym Sulinowie oraz zwiedziliśmy kirkut w Jastrowie.

Ślad całej wycieczki (ze zdjęciami) jest tutaj.

sobota, 24 sierpnia 2013

Konwersja z WordPressa do Bloggera

Kol. DM pisze sobie bloga używając WordPressa. Trochę go to kosztuje więc pojawiła się koncepcja żeby przeszedł na bezpłatnego Bloggera. Początkiem tej koncepcji jest oczywiście konwersja WP do formatu Bloggera.

Wpisy kol. DM zawierają tekst i dużo zdjęć oraz innych rysunków. Nie mam bladego pojęcia nt. WordPressa, ale dość szybko ustaliłem, że można wyeksportować treść posługując się stosowną funkcją dostępną z Kokpit→Narzędzia→Eksport (powstaje plik w formacie WordPress eXtended RSS -- WXR).

Plik WXR zapisujemy na dysku. Można go zamienić do formatu Bloggera korzystując z konwertera dostępnego tutaj. Jest wprawdzie napisane, że konwerter obsługuje pliki nie większe niż 1Mb, ale mój miał 4Mb i też poszło.

Plik kolegi DM zawiera tekst oraz prawidłowe linki do niektórych rysunków. Inne rysunki są wstawiane sprytnym czymś co nazywane jest shortcode (cf Shortcode API.) Nie wchodząc w szczegóły, zamiast rysunków w treści postu jest umieszczone np. coś takiego:


[nggallery id=506]

506 z kolei jest identyfikatorem zbioru rysunków, które fizycznie są przechowywane w katalogu:


wp-content/gallery

Każda galeria jest w oddzielnym katalogu, ale nazwami katalogów nie są identyfikatory typu 506 ale coś innego. Logując się do phpMyAdmin byłem w stanie ściągnąć całą bazę (w formacie SQL), w której siedzi WordPress. W jednej z tabel bazy znalazłem przypisanie id_galeriinazwa-katalogu-z-plikami


INSERT INTO `wp_ngg_gallery` (`gid`, `name`, `slug`,
`path`, `title`, `galdesc`, `pageid`, `previewpic`, `author`) VALUES
(17, 'gottardo_2', '', 'wp-content/gallery/gottardo_2', '', '', 0, 0, 1),
(16, 'gottardo_1', '', 'wp-content/gallery/gottardo_1', '', '', 0, 0, 1),
(15, 'nufenen', '', 'wp-content/gallery/nufenen', NULL, NULL, 0, 0, 1),
... itd ...

Czyli zawartość czegoś, co w treści wygląda jak:


[nggallery id=15]

Znajduje się w katalogu wp-content/gallery/nufenen.

Teraz ściągnąłem cały katalog wp-content na dysk lokalny wykorzystując ncftp


ncftp -u USER -p PASS HOST
get -R -T wp-content

Następnie zamieniłem nazwy plików w następujący sposób:


nr_galerii__nazwa_pliku

Pliki wysłałem na google za pomocą skryptu. Istotne jest to, że skrypt po załadowaniu, zwraca URL zdjęcia pod którym jest ono dostępne na koncie googla:


100__fra_07063.jpg https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnGM8VIJfmKu-dEbNbqqqNEV0DwoloVFw9uktPM6qJpmYSzKJITMugQqH2B0MeTAAFAgCX9o3vRmytf1UvdoBaA5TieHIgp3byMZFEtNmu9RfVCYzzN0xBdtq5wamOf_OMhqbHj4CxwYPt/
100__fra_07064.jpg https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPtWKcJyxiQsoE03R_7UAS-ScRhy-VtXpQ9swfpgaz6S8v_ypfliLJnIFDD92ae9Ctj31J_zLUaara8zC4pPp26Tkv2qzx2jZexmIdHMoX0qIdI9vuw3FE1oiNUCnZZwNSLkxVyYxUBCxX/
itd...

Czyli zdjęcie 100__fra_07063.jpg (oryginalnie należące go galerii o identyfikatorze równym '100') jest dostępne pod adresem:


https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnGM8VIJfmKu-dEbNbqqqNEV0DwoloVFw9uktPM6qJpmYSzKJITMugQqH2B0MeTAAFAgCX9o3vRmytf1UvdoBaA5TieHIgp3byMZFEtNmu9RfVCYzzN0xBdtq5wamOf_OMhqbHj4CxwYPt/

Uwaga: Album ze zdjęciami na koncie googla może zawierać maksymalnie 1000 zdjęć. Jeżeli zdjęć jest więcej trzeba utworzyć więcej albumów.

Skryptem Perla (wyrażenia regularne/regułowe) zamieniam każde [nggallery id=506] na stosowny ekwiwalent. Przykładowo:


&lt;div id='gid_g509'&gt;
&lt;span&gt;&lt; a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGXJg1WrcrBW1kJl3RJn9FPNSWk2hFjiC6dgFfslV3yjdpco2aIMEeYaQXQkirZuahaKiXCTQOqhcW7c1NQAXOpRBjCzyfQWiMptLCozUJy91d83j0gp2YncFkXvcwvHLaSPv3mfaxJJWo/"
imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"&gt;
&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGXJg1WrcrBW1kJl3RJn9FPNSWk2hFjiC6dgFfslV3yjdpco2aIMEeYaQXQkirZuahaKiXCTQOqhcW7c1NQAXOpRBjCzyfQWiMptLCozUJy91d83j0gp2YncFkXvcwvHLaSPv3mfaxJJWo/s128/509__1106_016.jpg"
height='85' /&gt;&lt;/a&gt;
&lt;/span&gt;
... itd ...

Pozostałe zdjęcia mają `prawdziwe URLe' (a nie jakieś shortcody), ale oczywiście URLe te są złe bo wskazują na starego hosta. Zdjęcia te (z `prawdziwymi URLami') są przechowywane w katalogu ./wp-content/uploads. Ze zdjeciami postępuję, tak jak w przypadku zdjęć z galerii: 1) wysyłam na konto google skryptem; 2) zmieniam oryginalne URLe na URLe z konta google (skryptem Perla).

Przed importem do Bloggera warto sprawdzić czy plik, który ma być zaimportowany jest well-formed:


xmllint plik-do-zaimportowania.xml

czwartek, 22 sierpnia 2013

Problematyczny Lightbox w Bloggerze

Kliknięcie zdjęcia lub obrazu zamieszczonego na blogu powoduje wyświetlenie go w nakładce na stronie. Jest to tzw. widok Lightbox. (cf. Widok Lightbox w Bloggerze):

<div>
<span>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU6xuLcKQQ-_ccqmRlM7tjekEMVB4Jgx2UvxS8wqL5154fLo383vlvKYmL4wvghW9wCu6nIyGyvW5eyEv4TNkScK3qc0zGJxZ4EOAC7Pb0YWWnBFGpDgcrmXwqNFP1au-D_nwrdBMPd78N/" 
 imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" 
 src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU6xuLcKQQ-_ccqmRlM7tjekEMVB4Jgx2UvxS8wqL5154fLo383vlvKYmL4wvghW9wCu6nIyGyvW5eyEv4TNkScK3qc0zGJxZ4EOAC7Pb0YWWnBFGpDgcrmXwqNFP1au-D_nwrdBMPd78N/s128/epl313_6040342.jpg" /></a>
</span>
<span>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8MsKjD61FOS9mOciTMc2srBAy9YeDnFfq05r_PPhFA8-psqNilfs5hkET159ViIBg07tw9kJBFT2Yy-aI7KplfVUNvmICu07pwX0Q2UPiQenTfmKh11P_ZrAs1_aRZjdeDiHTOyyy-gu6/" 
 imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" 
 src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8MsKjD61FOS9mOciTMc2srBAy9YeDnFfq05r_PPhFA8-psqNilfs5hkET159ViIBg07tw9kJBFT2Yy-aI7KplfVUNvmICu07pwX0Q2UPiQenTfmKh11P_ZrAs1_aRZjdeDiHTOyyy-gu6/s128/epl313_6040338.jpg" /></a>
</span>
<span>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA059OOx8ZLS7XqpiZ94iAZ7fdepsU3VAnGCeZ01SiDImelMrjHZwBd5g_o9shvBVB4Ut6x-_cfDrT51VvTc8KuQ7VlCYcZhOr0kEFenfurTKIs_rescLpAKu3-QbCtdwMmDEFaehvU3FM/" 
 imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" 
 src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA059OOx8ZLS7XqpiZ94iAZ7fdepsU3VAnGCeZ01SiDImelMrjHZwBd5g_o9shvBVB4Ut6x-_cfDrT51VvTc8KuQ7VlCYcZhOr0kEFenfurTKIs_rescLpAKu3-QbCtdwMmDEFaehvU3FM/s128/epl313_6040339.jpg" /></a>
</span>
<span>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0jT3S-QbEQ4sUexmIQoryhbi2C53tWrO0yHFrZyhRI-Q3naU_KxDipx0005eXd5t6WTgGwcpHnPDJyYWSG1en1R7aEmtpFxkJbc0tRWjQj3lr2_jNtfNn9B-FAVrtLenz8qLyizwxvOq1/" 
 imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" 
 src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0jT3S-QbEQ4sUexmIQoryhbi2C53tWrO0yHFrZyhRI-Q3naU_KxDipx0005eXd5t6WTgGwcpHnPDJyYWSG1en1R7aEmtpFxkJbc0tRWjQj3lr2_jNtfNn9B-FAVrtLenz8qLyizwxvOq1/s128/epl313_6040332.jpg" /></a>
</span>
</div>

Wynik jest następujący (kliknij w zdjęcie aby przejść do widoku Lightboksa):

W powyższym przykładzie zawartość atrybutów href i src jest prawie identyczna, bo różni się wyłącznie fragmentem s128/. Jest to przykład zastosowania sprytnego URLa, który spowoduje automagiczne przeskalowanie zdjęcia, tak aby dłuższy wymiar miał maksymalnie 128 pikseli. Jeżeli zamiast s128/ wstawimy s128-c/ albo s128-p/, to spowoduje to automagiczne wycięcie kwadratu o wymiarach maksymalnych 128 pikseli (crop). Przykład:

<div>
<span>
<a href="http://lh3.ggpht.com/…/epl313_6040338.jpg" 
 imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" 
 src="http://lh3.ggpht.com/…/s128-c/epl313_6040338.jpg" /></a>
</span>
<span>
<a href="http://lh3.ggpht.com/…/epl313_6040338.jpg" 
 imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" 
 src="http://lh3.ggpht.com/…/s128-p/epl313_6040338.jpg" /></a>
</span>

Symbolem oznaczono pominiętą część URLa (żeby nie wystawał na margines).

Wynik (kliknięcie w zdjęcie nie powoduje przejścia do widoku Lightboksa):

BTW: Liczba 128 nie jest magiczna, można przeskalować obrazek na inny wymiar.

Jeżeli zdjęcia są różnych wymiarów (portret/pejzaż), to s128-c/ (albo s128-p/) jest lepsze niż zwykłe s128, bo minaturki są jednakowej wielkości, tyle że Lightbox takie zdjęcia ignoruje. Czemu -- nie wiem... Nie wiem też jak to zmienić

Jako obejście problemu można przeskalować miniaturkę do jednakowej wysokości. Przykład:

<div>
<span>
<a href="https://lh4.googleusercontent.com/…/epl313_6040342.jpg" 
 imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85'
 src="https://lh4.googleusercontent.com/…/s128/epl313_6040342.jpg" /></a>
</span>
<span>
<a href="http://lh3.ggpht.com/…/epl313_6040338.jpg" 
 imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85'
 src="http://lh3.ggpht.com/…/s128/epl313_6040338.jpg" /></a>
</span>
<span>
<a href="http://lh6.ggpht.com/…/epl313_6040339.jpg" 
 imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85'
 src="http://lh6.ggpht.com/…/s128/epl313_6040339.jpg" /></a>
</span>
<span>
<a href="http://lh5.ggpht.com/…/epl313_6040332.jpg" 
 imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85'
 src="http://lh5.ggpht.com/…/s128/epl313_6040332.jpg" /></a>
</span>
</div>

Symbolem oznaczono pominiętą część URLa (żeby nie wystawał na margines).

Wynik jest następujący:

Kliknięcie w zdjęcie powoduje przejście do widoku Lightboksa.

Być może za czas jakiś problem zniknie. Wyświetlanie dla tego wpisu 10 zamiast 8 obrazków w widoku Lightboksa będzie tego dowodem:-)

środa, 31 lipca 2013

Wycieczka do Łeby

Wczoraj pojechaliśmy oglądać ruchome wydmy w Słowińskim Parku Narodowym. Wydmy są ruchome, bo przesuwają się na zachód czyli w stronę Łeby z prędkością kilku metrów na rok.

W celu obejrzenia wydm należy pojechać do Rąbki k. Łeby. Z Rąbki jeżdżą melexy albo można jechać rowerem lub iść pieszo. Trasa z Rąbki (około 6km) biegnie lasem przez teren Słowińskiego Parku Narodowego więc trzeba kupić bilet wstępu. Można też iść na wydmy brzegiem morza za darmo (uwaga na wiatr, bo często mocno wieje -- pod wiatr może być mało przyjemnie).

Na wydmy poszliśmy lasem a wróciliśmy Melexem (15 PLN od osoby).

Mniej więcej w połowie drogi między Rąbką a wydmami jest jeszcze wyrzutnia rakiet. Ekstra płatne miejsce -- pozostałość po niemieckim poligonie rakietowym. Nie wchodziliśmy, bo drogo, a atrakcja wydaje się wątpliwa.

Ślad z wycieczki (ze zdjęciami) jest tutaj.

środa, 24 kwietnia 2013

Time lapse: end of first experiment

Because my tomatoes are growing unexpectedly fast I had to stop capturing pictures quicker than I have planned as they do not fit in the frame.

So 2664 pictures was taken from 16.03 to 23.04 at 20 minutes interval. The video size is about 715 Mb and the video length is 3min 43 seconds (at 12 fps or 1:47 at 25 fps).

The videos are available at YouTube: 25 fps | 12 fps

niedziela, 21 kwietnia 2013

Augmenting video files with GPS data

This post describes in detail how to add GPS data to video file using `visual correlation' (see also: Video geocoding with gpsbabel).

Zebra crossing at Osowa
Crossing at Osowa

There is an easy way to augment video files with GPS data using GPSbabel. Since version 1.4 of GPSBabel is able to create a subtitle file from a GPS tracklog. The subtitle contains latitude/longitude/altitude as well as the time. With a simple Perl scripts I have added current speed (not particularly accurate however).

To convert GPX file to subtitles one have to execute:


gpsbabel -i gpx -f FILE.gpx -o subrip,video_time=hhmmss,gps_time=hhmmss,gps_date=YYYYMMDD -F FILE.srt

Where: video_time -- video position (relative to beginning of video) for which exact corresponding GPS timestamp is known. gps_time -- the time part of the GPS timestamp which corresponds to a known position in the video. gps_date -- the date part of the GPS timestamp which corresponds to a known position in the video.

On a video (see below) you can see that in 34 second I passed the zebra crossing. The zebra's crossing coordinates can be easily identified at Google Maps (cf. picture Crossing at Osowa). Now one have to search the GPX track for the point which is nearest to 54.429591/18.477973. I developed a simple Perl script for that purpose:


$ perl My_GPX_nearest_timestamp.pl -c 54.429591:18.477973 20130420.gpx
*** USAGE: My_GPX_nearest_timestamp.pl -c latitude:longitude GPX-file
*** Looking for 20 points near: 54.429591:18.477973 (lat/lon) ***
> 15.3758195746663 2013-04-20T10:27:09Z 54.429509640:18.477780819
> 22.0617273294607 2013-04-20T09:05:15Z 54.429701502:18.478256240
> 32.6458959467509 2013-04-20T10:27:15Z 54.429787332:18.478348190
> 43.3771684316959 2013-04-20T09:05:24Z 54.429531014:18.477310427
> 47.7727043140018 2013-04-20T09:05:11Z 54.429905936:18.478475260
> 63.903936892351 2013-04-20T10:27:00Z 54.429627573:18.476987137
> 70.171200323179 2013-04-20T09:05:28Z 54.429655485:18.476893930

One can stipulate from the above output that I passed a point 15,4 meters away from 54.429591/18.477973 at 10:27:09 GMT as well as I passed another point which is 22 meters away from 54.429591/18.477973 at 09:05:15. As I cycled back and forth along the same route the second point is valid, the first is accidentally closer but as the time is almost one and half hours later it is clear that I was there on returning home. So gpsbabel should be executed as follows (20130420.gpx contains GPX track):


gpsbabel -i gpx -f 20130420.gpx -o subrip,video_time=000034,gps_time=090515,gps_date=20130420 -F 20130420_1.srt

Speed is added with another very simple Perl script:


perl add_speed_2_srt.pl 20130420_1.srt > 20130420_1_S.srt

Just to remind: internally all GPS units record time/date using Coordinated Universal Time aka Greenwich Mean Time vel Zulu time (for army enthusiasts). What the unit displays is another matter (usually it displays local time).

poniedziałek, 15 kwietnia 2013

Pierwszy film poklatkowy

Ustaliłem empirycznie w jaki sposób podłączyć aparat do Raspberry Pi (do SheevaPlug zresztą też) żeby nic się nie zacinało. W skrócie:

  1. Używam kompakta Nikon S3000 (Canon A620 się zacinał/odłączał--sprzedałem go na Allegro).

  2. Podłączam aparat poprzez aktywny USB hub (kupiłem w tym celu cztero-portowy HUB firmy Vivanco).

  3. Po każdym zdjęciu wykonuję reset stosownego portu USB za pomocą programiku pn. usb_reset (zobacz tutaj oraz tutaj).

Kilka aparatów

Używam dwóch aparatów Nikon S3000 (kupionych na Allegro oczywiście), więc jest problem z ustaleniem który jest który:


$gphoto2 --auto-detect
Model Port
----------------------------------------------------------
Nikon Coolpix S3000 (PTP mode) usb:001,022
Nikon Coolpix S3000 (PTP mode) usb:001,021

Można użyć opcji --port usb:001,022 aby wykonać coś z ,,pierwszym'' aparatem oraz --port usb:001,021, aby dostać się do drugiego. Oczywiście umieszczenie numerów portów na-zicher w skryptach byłoby kiepskim pomysłem ponieważ nie są one ustalone, ale się zmienią jeżeli urządzenie zostanie odłączone/przyłączone ponownie. Lepszym sposobem zidentyfikowania aparatów jest wykorzystanie numeru seryjnego:


## $gphoto2 --get-config serialnumber --port PORT
## example
$gphoto2 --get-config serialnumber --port usb:001,022
Label: Serial Number
Type: TEXT
Current: 000047514512

Mam czarnego Nikona o numerze 000041076602 oraz różowego o numerze 000047514512. Używam następującego skryptu do wykonania zdjęcia określonym aparatem:


#!/bin/bash
PINK_CAM_ID='000047514512'
BLACK_CAM_ID='000041076602'

while test $# -gt 0; do
case "$1" in
-b|--black) REQ_CAM="$BLACK_CAM_ID";;
-p|--pink) REQ_CAM="$PINK_CAM_ID";;
esac
shift
done

## Nazwa pliku ze zdjęciem:
FILENAME="NIK`date +"%Y%m%d%H%M"`.jpg"

## Przejrzyj wszystkie podłączone aparaty:
while read PORT_ID
do
##echo $PORT_ID
## -n means string is non-empty
if [ -n "$PORT_ID" ] ; then
CAM_ID=`gphoto2 --get-config serialnumber --port $PORT_ID | awk '/Current:/ { print $2 }' `
if [ $CAM_ID = "$REQ_CAM" ] ; then
REQ_CAM_PORT="$PORT_ID"
##echo "*** Req Camera ID: #$CAM_ID."
fi
fi

done <<< "`gphoto2 --auto-detect | grep usb | awk '{ print $6}'`"

# Na wypadek błędu wyślij alarmowego SMSa
if [ -z "$REQ_CAM_PORT" ] ; then
echo "*** Error: Camera $REQ_CAM not found ***"
## sent a SMS cf http://pinkaccordions.homelinux.org/wblog/sms_alerts_with_google_calendar.html
sms_reminder.sh
exit 1
fi

## reset USB
REQ_CAM_PORT_DEVNAME=`echo $REQ_CAM_PORT | sed 's/^.*://' | sed 's/,/\//'`
usb_reset /dev/bus/usb/${REQ_CAM_PORT_DEVNAME}

LANG=C gphoto2 --port "$REQ_CAM_PORT" --force-overwrite --set-config flashmode=1 \
--set-config d002=4 \
--capture-image-and-download --filename "$FILENAME"

## reset USB (powtórny)
usb_reset /dev/bus/usb/${REQ_CAM_PORT_DEVNAME}

Właściwość d002 ustawia rozdzielczość zdjęcia (4 oznacza 2592x1944).

Wartość 1 właściwościflashmode wyłącza flash.

Z moich eksperymentów wynika, że bez wykonania usb_reset bateria aparatu nie jest doładowywana (czemu?) i po pewnym czasie z powodu braku zasilania aparat odłącza się.

Film

Po czterech tygodniach mam wystarczająco dużo zdjęć aby spróbować zrobić pierwszy film.

Najpierw konwertuję wszystkie zdjęcia do rozdzielczości 1920x1080 za pomocą programu convert (z zestawu ImageMagick) uruchamianego z ,,wewnątrz'' prostego skryptu Perla. Ponieważ nazwy plików wejściowych są konstruowane wg schematu NIKYYYYMMDDHHMM.jpg (gdzie YYY to rok, MM oznacza miesiąc, itd.) sortowanie alfabetyczne oznacza ustawienie ich także we właściwym porządku chronologicznym. Nazwy plików wynikowych są zaś konstruowane jako: hd1920_00001.jpg, hd1920_00002.jpg, itd.


#!/usr/bin/perl

opendir (DIR, ".");
my @files = sort { $a cmp $b } readdir(DIR);

while (my $file = shift @files ) {
if ($file =~ /.jpg$/) { $fileNo++;
$file_out = sprintf "hd1920_%05d.jpg", $fileNo;
print "$file -> $file_out\n";
system ("convert", $file, "-geometry", "1920x1080", "$file_out");
}
}

Okazało się, że jest dokładnie 1784 zdjęć:


$ls -l hd1920_0* | wc -l
1784

Konwersja trwała około 30 min na moim przeciętnym zupełnie PC-cie. Każdy plik wynikowy miał około 0,5Mb; wszystkie razem zajmowały około 0,85Gb:


$ls -l hd1920_0* | awk '{t+=$5}; END{print t}'
853332231

Film został wykonany za pomocą programu ffmpeg:


ffmpeg -r 12 -qscale 2 -i hd1920_%05d.jpg Tomato_12.mp4

Gdzie -r 12 oznacza liczbę klatek na sekundę (12 fps) a -qscale określa jakość (1 oznacza najlepszą, 32 najgorszą jakość).

Film ma około 450 Mb. Konwersja zajmuje około 3min (on my decent PC) a długość filmu to 2min i 29 sekundy.

Film przedstawia sadzonkę pomidora (Pinkaccordion oczywiście :-). Zdjęcia były robione co 20 minut od 16 marca do 11 kwietnia.

Są także wersje na 25 fps oraz 6 fps.