środa, 27 sierpnia 2008

Lejkowiec już jest

Dziś znalazłem pierwsze tegoroczne lejkowce. W Lasach Oliwskich wokół Sopotu.

Dopisane 4 września 2008: W lesie naokoło Sopotu jest wysyp borowików jakiego najstarsi ludzie nie pamiętają... I do tego pełno lejkowców, które mało kto zbiera.

Apache Velocity DocBook Framework

Wymieniony w tytule Apache Velocity DocBook Framework (AVDF) to sterowany antem zbiór narzędzi do zamiany dokumentów DocBook na HTML/PDF. Konkretnie tymi narzędziami są Fop (w wersji 0.20.5), Saxon i szablony XSL N. Walsha. Po ściągnięciu archiwum .zip lub .tgz ze strony projektu i rozpakowaniu, na dysku pojawi się następująca struktura katalogów:


<korzeń instalacji AVDF>
|
+---- build-docbook.xml
+---- project.properties
+-- src
+-- styles

+-- docs
+-- build.xml
+-- project.properties
+-- src
+--css
+--docbook
| +--<projekt>
+-- images
+-- target
+-- lib

Katalog lib zawiera fopa i saxona plus niezbędne dodatkowe aplikacje. Katalog src/styles zawiera szablony ,,startowe'', uruchamiane przy zamianie plików XML na HTML i PDF. [Tu trzeba dłubnąć żeby dokonać polonizacji.] Wreszcie dokument ,,źródłowy'' ma być obowiązkowo umieszczony w katalogu docs/src/docbook/<projekt>. Pliki z rysunkami zaś muszą być umieszczone w docs/src/images. Wynik transformacji będzie się znajdował w docs/target/<projekt>. Dodatkowo w docs/src/css znajduje się szablon CSS wykorzystywany przez wygenerowane pliki HTML.

Polonizacja wymaga: 1) ściągnięcia następującego archiwum .zip i rozpakowania jego zawartości w tak sposób aby została dodana do oryginalnej dystrybucji AVDF (kilka plików zostanie nadpisanych); 2) wykonania polecenia ant -f build-docbook.xml dbf.init z poziomu korzenia instalacji AVDF. Tyle -- powinno działać. Reszta tego wpisu dotyczy szczegółów: co, jak i dlaczego. Można nie czytać...

Pliki build-docbook.xml zmieniłem tak, że dodałem do wywołania fopa argument -c plik.cnf, tj.:


<java classname="org.apache.fop.apps.Fop" fork="true" maxmemory="256m"
dir="${basedir}" classpathref="dbf.classpath">
<arg value="${pdf.target.file}.xml"/>
<arg value="${target.dir}/${docbook.dir}/pdf/${docbook.file}.pdf"/>
<arg value="-c"/>
<arg value="${dbf.basedir}/conf/userconfig.xml"/>

</java>

Parametr ${dbf.basedir} to katalog główny, w którym znajduje się cały framework. Dodany katalog conf/ zawiera plik konfiguracyjny fopa + pliki metryczne fontów. Same fonty (pliki .ttf) są w katalogu fonts/. Dodanie do AVDC fontów powoduje, że całość jest gotowa do użycia w każdym systemie. [Można oczywiście tak skonfigurować AVDF żeby używał fontów systemowych]. Z kilku zestawów rozpowszechnianych bezpłatnie fontów truetype (Core fonts for the Web (Nie najwyższej jakości i do tego ich rozpowszechnianie podlega pewnym ograniczeniom), TeX Gyre, Quasi, STIX (tylko jedna odmiana -- brak kroju bezszeryfowego i kroju typu monospace), DejaVue) zdecydowałem się na fonty Quasi. Wprawdzie są one już obsolete ale działają z fopem a fonty TeX Gyre, które są lepszą wersją Quasi -- nie bardzo. [Są problemy nawet po konwersji OTF->TTF.]

Położenie fontów w systemie jest określone w pliku conf/userconfig.conf. Ponieważ ścieżka do fontu nie może być zaszyta na zicher, plik conf/userconfig.conf rozpoczyna się od następującej deklaracji DOCTYPE:


<!DOCTYPE configuration SYSTEM "config.dtd" [
<!ENTITY fop.home "@dbf.basedir@">
<!ENTITY ttf.dir "@dbf.basedir@/fonts">

Napisy @dbf.basedir@ są zamieniane na to co trzeba po uruchomieniu ant -f build-docbook.xml dbf.init (trzeba to zrobić dokładnie raz). Cel (target) dbf.init wygląda zaś następująco:


<target name='dbf.init' description='Adjust some system specific files'>

<copy file="${basedir}/conf/userconfig.conf" tofile='${basedir}/conf/userconfig.xml'
overwrite="true" failonerror="false" filtering="on" >
<filterset>
<filter token="dbf.basedir" value="${basedir}"/>
</filterset>
</copy>
</target>

Jak widać plik conf/userconfig.conf jest kopiowany do conf/userconfig.xml a dodatkowo napisy @dbf.basedir@ są zamieniane na bieżącą wartość ${basedir}. To się nazywa token substitution (cf. Using Ant as a Text Substitution Preprocessor).

Ostatni etap polonizacji AVDF to poprawienie plików html.xsl, htmlsingle.xsl z katalogu src/styles. Dodałem do plików html.xsl oraz htmlsingle.xsl:


<xsl:output method="html"
encoding="utf-8"
indent="no"
saxon:character-representation="native;decimal"
xmlns:saxon="http://icl.com/saxon" />

<!-- zapisuje znaki `natywnie' a nie jako encje (domyślnie) -->
<xsl:param name="chunker.output.encoding" select="'utf-8'"/>
<xsl:param name="saxon.character.representation" select="'native;decimal'"/>

A do pliku pdf.xsl (z tego samego katalogu):


<!-- przełącza się na fonty Quasi, domyślnym fontem szeryfowym jest
QuasiPalatinoTTF, zmień na QuasiTimesTTF jeżeli ma być Times New Roman -->
<xsl:param name="sans.font.family" select="'QuasiSwissTTF'"/>
<xsl:param name="title.font.family" select="'QuasiSwissTTF'"/>
<xsl:param name="body.font.family" select="'QuasiPalatinoTTF'"/>
<xsl:param name="monospace.font.family" select="'QuasiCourierTTF'"/>

Powyższe to minimum: polskie znaki są prawidłowo wyświetlane na ekranie oraz -- co ważne -- zapisywane w pliku jako sekwencje UTF a nie w postaci encji (tj. &oacute;). Jeżeli układ graficzny dokumenty komuś nie odpowiada, no to musi jeszcze bardziej zmodyfikować pliki .xsl i/lub CSS (oczywiście szablony CSS dotyczą wyłącznie dokumentów HTML).

wtorek, 26 sierpnia 2008

Wywiad z Knuthem w CACM

W numerach 7--8 Communications of the ACM jest wywiad z Knuthem (ściśle mówiąc: vol. 51 nos 7--8, 2008). Pierwsza część jest teraz nawet dostępna on-line. Fragment: Then a startup company came to me and said, [...] ``Name your price.'' I said ``Oh, okay, $100,000,'' assuming that this was [outrageous]. The guy didn't blink. He agreed. I didn't blink either. I said, ``I'm not going to do it. I just thought that was an impossible number.'' At that point I made the decision in my life that, I wasn't going to optimize my income.

niedziela, 24 sierpnia 2008

Konwersja OTF na TTF

Poniżej skrypt zamieniający font w formacie OpenType na TrueType.


#!/usr/local/bin/fontforge
# Quick and dirty hack: converts a font to truetype (.ttf)
# cf. http://www.stuermer.ch/blog/convert-otf-to-ttf-font-on-ubuntu.html
Print("Opening "+$1);
Open($1);
Print("Saving "+$1:r+".ttf");
Generate($1:r+".ttf");
Quit(0);

Czy konwersja jest w 100% OK nie jest do końca pewne bo podobno gubiona jest informacja na temat par kernowych.

środa, 20 sierpnia 2008

Przewyższenie i profil śladu

Etrex mimo, że nie ma barometru dość sensownie podaje wysokość npm. Jakoś do tej pory umknęło to mojej uwadze.

Poniżej skrypt wyznaczający długość trasy, łączną sumę wysokości podjazdów (przewyższenie, aka cumulative elevation gain) oraz rysujący profil w postaci pliku PNG. To ostatnie niekoniecznie jest sensowne, bo GD::Graph::lines traktuje dane z osi OX jako napisy a nie liczby i w związku z tym odstępy między poszczególnymi punktami na tej osi są równe. Zamiast GD::Graph::lines trzeba użyć czegoś innego, np. Chart::Graph::Gnuplot.


#!/usr/bin/perl
#
# Tworzy plik PNG przedstawiający profil śladu (wysokość mnpm)
# z danych podanych (jako argument skryptu) w pliku GPX
# tprzechlewski[_at_]gmail.com sierpień/2008
#
use XML::LibXML;
use Geo::Distance;
use Getopt::Long;

GetOptions( 'log' => \$print_log, 'pic' => \$print_pic, );

$usage = "Usage: $0 [-p | -l] plik.gpx ;; -p generate PNG file ; -l show log.\n";

my $geo = new Geo::Distance;

my $file = shift || die "$usage";

my $parser = XML::LibXML->new;

open my $fh, $file || die "problems...";

$doc = $parser->parse_fh($fh);

my @tracks = $doc->getElementsByTagName('trk');
my $ptnum=0;

for $tx (@tracks) {

@segments = $tx->getChildrenByTagName('trkseg');
if ( $name = $tx->getChildrenByTagName('name')->[0] ) { # pierwszy element to nazwa śladu
if ($print_log) { print "<!-- track:: ", $name->textContent(), " -->\n"; }
}

for $sx (@segments) {

@points = $sx->getChildrenByTagName('trkpt');

for $px (@points) {
@data = $px->getChildrenByTagName('*');
@attrs = $px->attributes();

if ($print_log) { print "-> " ; }

for $dx (@data) {
if ($print_log) { print $dx->nodeName, " = ", $dx->textContent(), " ; " ; }
if ($dx->nodeName eq 'ele') { $ele = $dx->textContent();
push @Elevations, $ele;
}
}

for $ax (@attrs) {
if ($print_log) { print " ", $ax->nodeName, " = ", $ax->getValue() ; }
if ($ax->nodeName eq 'lon') { $lon = $ax->textContent() }
elsif ($ax->nodeName eq 'lat') { $lat = $ax->textContent() }
}

if ($print_log) { print " ;;\n"; }

if ( $ptnum > 0 ) {
$curr_dist = $geo->distance( "meter", $plon, $plat => $lon, $lat );
$dist += $curr_dist ;
push @Distances, $curr_dist;
if (($ele_diff = $ele - $pele ) > 0) { $totalEleGain += $ele_diff ; }

} else {
push @Distances, 0;
}

$plon = $lon; $plat = $lat ; $pele = $ele ; $ptnum++;
}
}
}

# http://en.wikipedia.org/wiki/Cumulative_elevation_gain (przewyższenie):
printf "*** Dist: %.1f meters *** EleGain: $totalEleGain ***\n", $dist;

## Drukowanie profilu trasy

unless ( $print_pic ) { exit 0 }

use GD::Graph::lines;
use POSIX; # floor

my $img_file = "${file}.png" ;

my @data = (\@Distances, \@Elevations, );

my $mygraph = GD::Graph::lines->new(400, 300);

# skip some dates to avoid label overlapping on X-axis:
my $x_factor = floor (($#Distances + 1) / 10 ) + 2;
print "$#Distances observations. X-axis labels printed evey ${x_factor}th one!\n";

$mygraph->set_text_clr('black');
$mygraph->set(
x_label => 'Dist',
y_label => '#',
title => "# Elev",
# Draw datasets in 'solid', 'dashed' and 'dotted-dashed' lines
line_types => [1, 1, ],
# Set the thickness of line
line_width => 2,
# Set colors for datasets
dclrs => ['blue', 'red', 'cyan'],
#x_tick_number => 'auto',
x_label_skip => $x_factor,
transparent => 0, ## non-transparent
bgclr => 'white',
fgclr => 'black',
borderclrs => 'black',
boxclr => '#ede7e7',
labelclr => 'black',
#axislabelclr,
legendclr => 'black',
) or warn $mygraph->error;

$mygraph->set_legend_font(GD::gdMediumBoldFont);
$mygraph->set_legend('ele', 'ele2', '???');

my $myimage = $mygraph->plot(\@data) or die $mygraph->error;

## for cgi script uncomment:
##print "Content-type: image/png\n\n";

open ( IMG, ">$img_file") or die " *** Problems opening: $img_file ***" ;

print IMG $myimage->png;

close (IMG);

##print "@Distances\n"; ## debug

##

Wykorzystuję od jakiegoś czasu XML::LibXML. W skryptach do obsługi flickr.com korzystałem z XML::Simple ale do parsowania plików GPX ten pakiet się nie nadaje -- nie zachowuje porządku elementów (bo je czyta do hasza). W oczywisty sposób porządek punktów na śladzie nie może być dowolny.

A tutaj ktoś zrobił coś podobnego tyle, że używając Pythona.

wtorek, 19 sierpnia 2008

7 dni w Świdnicy

To była ciekawa wycieczka. Zwiedzaliśmy okolice Świdnicy oraz byliśmy na Śnieżce i w Górach Stołowych (Szczeliniec/Teplice). Wracając z Teplic zwiedziliśmy klasztor w Broumovie.

Doszło do niewielkiego bałaganu w zbiorze zdjęć przywiezionych z tygodniowego pobytu w Świdnicy. Zdjęcia z kilku dni przegrałem do jednego katalogu i hop, bez zbędnego myślenia uruchomiłem skrypt gpsPhoto.pl aby dodać współrzędne geograficzne. Po tej operacji czas modyfikacji każdego pliku ze zdjęciem się zmienił no i był mały kłopot z szybkim ustaleniem w którym dniu dane zdjęcie było zrobione. Oczywiście ta informacja nie została utracona, bo jest wartością pola DateTimeOriginal, tyle że ,,z poziomu'' wiersza poleceń tego nie widać.

Już chciałem jakiś skrypt klecić żeby pobierał DateTimeOriginal a następnie zmieniał czas modyfikacji pliku, ale w dokumentacji exiftool znalazłem gotowy przykład dokładnie robiący to, co chciałem osiągnąć:


exiftool "-Directory<DateTimeOriginal" -d "%Y-%m-%d" KATALOG

Powyższe przeniesie wszystkie pliki .jpeg z katalogu KATALOG do katalogów o nazwach postaci rok-miesiąc-dzień. Daty modyfikacji pliku to nie zmienia, ale samo uporządkowanie w odpowiednich katalogach w zupełności mi wystarczy.

Mój GPS Logger (używam Iblue 747) dwa razy zarejestrował ślad zawierający kolosalne odchylenie (biegun północny, tj. lat="90.000000000" lon="0.000000000"). Wcześniej nie obserwowałem tego fenomenu, tj. mówiąc dokładnie Etrex H nagminnie oszukuje podając np. średnią/maksymalną prędkość, ale zwykle zgrany ślad jest OK. Próbowałem poszukać jakiś automatycznych sposobów usunięcia takich anomalii ale nie znalazłem. Ostatecznie problem jest źle zdefiniowany więc automatem się nie da. Potrzebny jest edytor i wizualna inspekcja (np. MapSource ale to wyłącznie windziana aplikacja). Z braku takowego ślad wyświetlam na Google Maps a rażące odchylenia usuwam patrząc na współrzędne. Na moje potrzeby super korekta śladu nie jest potrzebna. Tak nawiasem mówiąc wspólnym mianownikiem Loggera i Etrexa jest chip firmy MTK. Ciekawe czy wszystkie czułe GPSy typu Sirf3/MTK robią takie błędy czy tylko MTK?

Ślady w formacie KML ze zdjęciami umieściłem tutaj: świdnica--książwlk.sowa--ludwikowiceszczeliniec--wambierzycekarpacz--śnieżkakompleks--osówka+zamek grodnoteplice--broumovślęża--tąpadła--sulistrowiczki

poniedziałek, 4 sierpnia 2008

piątek, 1 sierpnia 2008

Pedometer

Jak nie zapiszę, to zapomnę. Już kiedyś znalazłem serwis pozwalający on-line na tworzenie tras (routes) w formacie GPX za pomocą klikania w GoogleMaps. Nie zapisałem i oczywiście nie mogłem go znaleźć przez dłuższą chwilę. W końcu jest: http://www.gmap-pedometer.com/. Moja przykładowa trasa: Brodnica-BachotekUMK. IMHO czasami może się okazać przydatne...

Planowanie tras w www.gmap-pedometer.com/

Trasy z google maps zaimportowane do odbiornika GPS są do kitu... Są zbyt proste i bez Gmapy sprowadzają się do linii prostej, łamanej w kilku miejscach. Dużo lepsze efekty daje http://www.gmap-pedometer.com/. Należy także zainstalować bookmarklet GMapToGPX, który -- przynajmniej w Firefoksie -- pojawi się jako guzik na pasku zakładek. Trasę należy wyklikać, a jak już jest gotowa trzeba nacisnąć GMapToGPX. W oddzielnym oknie zostanie wyświetlony dokument GPX, który metodą copy-paste wkleja się do pliku. Teraz należy załadować trasę -- w przypadku mojego Legenda HCx, gpsbabela i FC8, wygląda to następująco:


gpsbabel -r -i gpx -f plik.gpx -o garmin -x simplify,count=225 -F /dev/ttyUSB0

Ważny jest filtr -x simplify,count=225, bo trasy muszą być krótsze od 250 punktów albo zostaną ucięte (co urządzenie zasygnalizuje komunikatem Route Truncated). Dałem limit 225 mogłem 250... być może należy dać jeszcze mniej żeby Garmin nie piszczał non-stop.

Powyższe w praktyce sprawdziło się całkiem całkiem... Przykładowo moja trasa do Gdyni-Oksywia, naokoło przez las. (Na czerwono planowana na niebiesko zrealizowana.) W miejscu gdzie chciałem pojechać wzdłuż Estakady Kwiatkowskiego się okazało, że nie da rady (rura ciepłownicza); na GoogleMaps była tam zaznaczona ścieżka stąd takie nierealistyczne plany.