Weblog Tomasza Przechlewskiego [Zdjęcie T. Przechlewskiego]


scrum
random image [Photo gallery]
Zestawienie tagów
1-wire | 18b20 | 1wire | 2140 | 3rz | adamowicz | afera | alsamixer | amazon | amber | amman | anniversary | antypis | apache | api | applebaum | arm | armenia | astronomy | asus | atom.xml | awk | aws | bachotek | bakłażan | balcerowicz | balta | banan | bash | batumi | berlin | białowieża | białystok | bibtex | bieszczady | birzeit | biznes | blogger | blogging | blosxom | bme280 | bono | borne-sulinowo | breugel | bt747 | budapeszt | budyniowo | budyń | bursztyn | campagnolo | canon | cedewu | chaos | chello | chiller | chillerpl | chown | christophe dominici | chujowetaśmy | ciasto | cmentarz | contour | coronavirus | covi19 | covid | covid19 | cron | css | csv | cukinia | curl | cycling | d54250wykh | darkages | dbi | debian | dejavu | dhcp | dht22 | dia | docbook | dom | dp1500 | ds18b20 | duda | dulkiewicz | dulkiewiczowa | dyndns | dynia | ebay | economy | ecowitt | ekonomia | elka | elm | emacs | emacs23 | english | ep | erasmus | erasmusplus | ess | eu | eurostat | excel | exif | exiftool | f11 | fc | fc11 | fc15 | fc29 | fc5 | fc8 | fedora | fedora21 | fenix | ffmpeg | finepix | firefox | flickr | folau | fontforge | fontspec | fonty | food | fop | forms | foto | france | francja | fripp | froggit | fuczki | fuji | fuse | gammu | garden | garmin | gas | gawk | gazwyb | gdańsk | gdynia | gender | geo | geocoding | georgia | gft | ggplot | ghost | git | github | gmail | gmaps | gnokii | gnus | google | google apps script | googlecl | googleearth | googlemaps | gotowanie | gphoto | gphoto2 | gps | gpsbabel | gpsphoto | gpx | gpx-viewer | greasemonkey | gruzja | grzyby | gus | gw1000 | haldaemon | handbrake | helsinki | hhi | historia | history | hitler | holocaust | holokaust | hp1000se | hpmini | humour | iblue747 | ical | iiyama | ikea | imagemagick | imap | inkscape | inne | internet | j10i2 | javascript | jhead | jifna | jordania | k800i | kajak | kamera | karob | kibbeh | kleinertest | kml | kmobiletools | knuth | kociewie kołem | kod | kolibki | komorowski | konwersja | krutynia | krynki | kuchnia | kurski | kłamstwo | latex | latex2rtf | latex3 | lcd | legend | lenny | lesund | lewactwo | lgbt-folly | liban | liberation | linksys | linux | lisp | lisrel | litwa | lizbona | logika | ltr | lubowla | lwp | lwów | m2wś | malta | mapquest | mapsource | maradona | marchew | marimekko | marvell | math | mathjax | mazury | mbank | mediolan | mencoder | mevo | mex | mh17 | michalak | michlmayr | microsoft | monitor | mp4box | mplayer | ms | msc | mssql | msw | mswindows | mtkbabel | museum | muzyka | mymaps | mysql | mz | nafisa | nanopi | natbib | navin | neapol | nekrolog | neo | neopi | netbook | niemcy | niemieckie zbrodnie | nikon | nmea | nowazelandia | nuc | nxml | oauth | oauth2 | obituary | ocr | odessa | okular | olympus | ooffice | ooxml | opera | osm | otf | otftotfm | other | ov5647 | overclocking | ozbekiston | padwa | palestyna | panoramio | paryż | pdf | pdfpages | pdftex | pdftk | pedophilia | perl | photo | photography | pi | picasa | picasaweb | pim | pine | pis | pit | pizero | plain | plotly | pls | plugin | po | podcast | podlasie | podróże | pogoda | politics | polityka | polsat | portugalia | postęp | powerpoint | połtawa | prelink | problem | propaganda | pseudointeligencja | pstoedit | putin | python | pywws | r | r1984 | radio | random | raspberry | raspberry pi | raspberrypi | raspbian | refugees | relaxng | ridley | router | rower | rowery | roztocze | rpi | rsync | rtf | ruby | rugby | rumunia | russia | rwc | rwc2007 | rwc2011 | rwc2019 | ryga | rzym | salerno | samba | sds011 | selenium | sem | senah | sernik | sheevaplug | sienkiewicz | signature | sikorski | sks | skype | skytraq | smoleńsk | sqlite | srtm | sshfs | ssl | staszek wawrykiewicz | statistcs | statistics | stats | statystyka | stix | stretch | supraśl | suwałki | svg | svn | swanetia | swornegacie | szwajcaria | słowacja | tallin | tbilisi | terrorism | tesseract | tex | texgyre | texlive | thunderbird | tomato | totalnaopozycja | tourism | tramp | trang | transylwania | truetype | trzaskowski | ttf | turcja | turkey | turystyka | tusk | tv | tv5monde | tweepy | twitter | tykocin | typetools | ubuntu | uchodźcy | udev | ue | ukraina | umap | unix | upc | updmap | ups | utf8 | uzbekistan | varia | video | vienna | virb edit | virbedit | vostro | wammu | wdc | wdfs | weather | weathercloud | webcam | webdav | webscrapping | weewx | wenecja | wh2080 | wiedeń | wikicommons | wilno | win10 | windows | windows8 | wine | wioślarstwo | wojna | word | wordpress | wrt54gl | ws1080 | wtyczka | wunderground | ww2 | www | wybory | wybory2015 | włochy | węgry | xemex | xetex | xft | xhtml | xine | xml | xmllint | xsd | xslt | xvidtune | youtube | yum | zaatar | zakopane | zakupy | zawodzie | zdf | zdrowie | zeropi | zgarden | zgony | zprojekt | łeba | łotwa | świdnica | żywność
Archiwum
06/2023 | 02/2023 | 01/2023 | 11/2022 | 10/2022 | 09/2022 | 07/2022 | 06/2022 | 04/2022 | 03/2022 | 02/2022 | 12/2021 | 09/2021 | 03/2021 | 01/2021 | 12/2020 | 11/2020 | 10/2020 | 09/2020 | 08/2020 | 07/2020 | 04/2020 | 03/2020 | 02/2020 | 01/2020 | 12/2019 | 11/2019 | 10/2019 | 09/2019 | 08/2019 | 07/2019 | 06/2019 | 04/2019 | 02/2019 | 01/2019 | 12/2018 | 11/2018 | 10/2018 | 09/2018 | 08/2018 | 07/2018 | 05/2018 | 04/2018 | 03/2018 | 02/2018 | 01/2018 | 11/2017 | 10/2017 | 09/2017 | 08/2017 | 07/2017 | 06/2017 | 05/2017 | 04/2017 | 03/2017 | 02/2017 | 01/2017 | 12/2016 | 11/2016 | 10/2016 | 09/2016 | 08/2016 | 06/2016 | 05/2016 | 04/2016 | 02/2016 | 12/2015 | 11/2015 | 09/2015 | 07/2015 | 06/2015 | 05/2015 | 02/2015 | 01/2015 | 12/2014 | 09/2014 | 07/2014 | 06/2014 | 04/2014 | 02/2014 | 01/2014 | 12/2013 | 11/2013 | 10/2013 | 09/2013 | 08/2013 | 07/2013 | 05/2013 | 04/2013 | 03/2013 | 02/2013 | 01/2013 | 12/2012 | 11/2012 | 10/2012 | 09/2012 | 08/2012 | 07/2012 | 05/2012 | 03/2012 | 02/2012 | 01/2012 | 12/2011 | 11/2011 | 10/2011 | 09/2011 | 08/2011 | 07/2011 | 06/2011 | 05/2011 | 04/2011 | 03/2011 | 02/2011 | 01/2011 | 12/2010 | 11/2010 | 10/2010 | 09/2010 | 08/2010 | 07/2010 | 06/2010 | 05/2010 | 04/2010 | 03/2010 | 02/2010 | 01/2010 | 12/2009 | 11/2009 | 10/2009 | 09/2009 | 08/2009 | 07/2009 | 06/2009 | 05/2009 | 04/2009 | 03/2009 | 02/2009 | 01/2009 | 12/2008 | 11/2008 | 10/2008 | 09/2008 | 08/2008 | 07/2008 | 06/2008 | 05/2008 | 04/2008 | 03/2008 | 02/2008 | 01/2008 | 12/2007 | 11/2007 | 10/2007 | 09/2007 | 08/2007 | 07/2007 |
O stronie
wykorzystywany jest blosxom plus następujące wtyczki: tagging, flatarchives, rss10, lastbuilddatexhtmlmime. Niektóre musiałem dopasować nieco do swoich potrzeb. Więcej o blosxom jest tutaj
Subskrypcja
RSS 1.0
Wyświetlanie śladów GPX (jest życie bez GoogleMaps)

Mój sposób na wyświetlanie na mapie danych w formacie GPX do tej pory polegał na uruchomieniu skryptu w PHP, który to skrypt pobierał nazwę pliku GPX, a następnie wypisywał stosowny plik HTML. W związku ze zmianą licencji przez Google, skypt przestał działać. Zmiana na OSM (Open Street Map) okazała się banalnie prosta:

<html>
<head>
  <title>GPX view facility</title>
  <link rel="stylesheet" href="/leaflet/leaflet.css" />
  <script src="/leaflet/leaflet.js"></script>
  <script src="/leaflet/gpx.js"></script>
  <style> #map { width: 960px; height:500px; } </style>
</head>
<body>
<div id="map"></div>
<script>

    var map = L.map('map',{
    center: [54.4, 18.4],
    zoom: 11
    });

    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);

    <?
    $trasa = $_GET['trasa'];
    if ($trasa != '')  { print "var gpx = 'http://pinkaccordions.homelinux.org/Geo/gpx/$trasa.xml'"; }
   ?>

    new L.GPX(gpx, {async: true}).on('loaded', function(e) {
    map.fitBounds(e.target.getBounds());
    }).addTo(map);
</script>
</body>

Żeby powyższe działało trzeba pobrać i skopiować do właściwych katalogów: leaflet oraz plugin leaflet-gpx. Że działa można sprawdzić tutaj.

url | Mon, 17/09/2018 11:29 | tagi: , ,
Geotagowanie zdjęć za pomocą ExifTool

Uwaga: Poniżej zakłada się, że program ExifTool jest używany w środowisku MS Windows.

Pobierz plik ExifTool.exe i umieść go w jakimś katalogu (SomeDir)

Otwórz wiersz polecenia (przejdź do wyszukiwania systemu Windows, wpisz polecenie cmd i otworzy się okno)

W oknie wiersza polecenia zmień katalog na SomeDir.

Geotagowanie zdjęć przy użyciu śladu w formacie GPX

Zdjęcia zrobione z nowoczesnych smartfonów są już geotagowane (jeśli opcja Lokalizacja jest włączona).

Dla aparatów nie wyposażonych w GPS, geotagowanie jest możliwe jeżeli dysponujemy śladem (zarejestrowanym za pomocą zewnętrznego odbiornika GPS.)

Synchronizowanie zdjęć ze śladem GPS

Pobierz plik GPX z odbiornika/rejestratora GPS (dokładna procedura zależy od typu odbiornika GPS) do katalogu SomeDir.

Zrób zdjęcie odbiornika GPS/ekranu lub wyświetlacza, na którym wyświetlany jest zegar z dokładnym czasem (na przykład ekranu komputera/smartfona).

Wpisz w wierszu poleceń:

exiftool -DateTimeOriginal IMG_20171021_113121.jpg 
Date/Time Original : 2017:10:29 11:52:46

Wartość taga DateTimeOriginal określa, że zdjęcie zostało wykonane o 11:52:46. Dokładny czas odczytujemy ze zdjęcia. Załóżmym że czas na zdjęciu wynosi 11:50:19 CET. Zatem DateTimeOriginal jest równe 11:52:46 CET a czas dokładny 11:50:19 CET, stąd różnica 11:32:46 - 11:50:19 = 2min 27s.

Wszystkie odbiorniki GPS wewnętrznie używają/rejestrują wyłącznie używając czasu UTC, nawet jeśli na ekranie pokazywany jest czas lokalny (Por. UTC_offset). Zwykle aparat wewnętrznie korzysta z czasu lokalnego. Jeśli tak jest, należy zadeklarować odpowiednie przesunięcie UTC_offset (w przeciwnym przypadku exiftool używa systemowego przesunięcia czasu względem UTC).

Skopiuj wszystkie zdjęcia do programu SomeDir.

Wpisz w wierszu poleceń:

exiftool -geosync=-02:27 '-geotime<${DateTimeOriginal}+01:00' -geotag my_gps.log SomeDir

Tag Geotime określa punkt w czasie dla którego obliczana jest pozycja GPS. Zapis -geotime<${DateTimeOriginal}+01:00 oznacza, że pozycja jest obliczana według czasu z taga DateTimeOriginal. Zaś dodanie +01:00 ustala CET jako strefę czasową (Przesunięcie +01:00 względem UTC)

Tag Geosync dodaje dodatkowy dryft czasu, jak wyjaśniono powyżej.

Utworzenie pliku KML

Opcja -p służy do generowania plików GPX i/lub KML.

exiftool -fileOrder gpsdatetime -p gpx.fmt SomeDir > SomeTripPhotos.gpx

albo

exiftool -fileOrder gpsdatetime -p kml.fmt SomeDir > SomeTripPhotos.kml

Pliki KML mogą być importowane do Map Google i Google Earth.

Uwaga: jeżeli mapa ma być wyświetlona nie lokalnie, ale na jakimś serwerze WWW (na przykład: http://www.gpsvisualizer.com/), zdjęcia muszą być przesłane na jakiś serwer, a następnie w pliku KML/GPX należy poprawić linki do tychże zdjęć. Dokładna procedura zależy od tego, który serwer jest używany do przechowywania zdjęć.

Przykładowe pliki formatów

gpx.fmt

#[HEAD]<?xml version="1.0" encoding="utf-8"?>
#[HEAD]<gpx version="1.0"
#[HEAD] creator="ExifTool $ExifToolVersion"
#[HEAD] xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
#[HEAD] xmlns="http://www.topografix.com/GPX/1/0"
#[HEAD] xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
#[BODY]<wpt lat="$gpslatitude#" lon="$gpslongitude#">
#[BODY]  <name>$directory/$filename</name>
#[BODY]  <ele>$gpsaltitude#</ele>
#[BODY]  <desc>$directory/$filename</desc>
#[BODY]  <link href="$directory/$filename"><type>large</type></link>
#[BODY]  <link href="$directory/$filename"><type>thumbnail</type></link>
#[BODY]  <time>$gpsdatetime</time>
#[BODY]</wpt>
#[TAIL]</gpx>

kml.fmt

#[HEAD]<?xml version="1.0" encoding="UTF-8"?>
#[HEAD]<kml xmlns="http://earth.google.com/kml/2.0">
#[HEAD]  <Document>
#[HEAD]    <name>My Photos</name>
#[HEAD]    <open>1</open>
#[HEAD]    <Style id="Photo">
#[HEAD]      <IconStyle>
#[HEAD]        <Icon>
#[HEAD]          <href>http://maps.google.com/mapfiles/kml/pal4/icon38.png</href>
#[HEAD]          <scale>1.0</scale>
#[HEAD]        </Icon>
#[HEAD]      </IconStyle>
#[HEAD]    </Style>
#[HEAD]    <Folder>
#[HEAD]      <name>Waypoints</name>
#[HEAD]      <open>0</open>
#[BODY]      <Placemark>
#[BODY]        <description><![CDATA[<br/><table><tr><td>
#[BODY]        <img src='$directory/$filename'
#[BODY]          width='$imagewidth' height='$imageheight'>
#[BODY]        </td></tr></table>]]></description>
#[BODY]        <Snippet/>
#[BODY]        <name>$filename</name>
#[BODY]        <styleUrl>#Photo</styleUrl>
#[BODY]        <Point>
#[BODY]          <altitudeMode>clampedToGround</altitudeMode>
#[BODY]          <coordinates>$gpslongitude#,$gpslatitude#,0</coordinates>
#[BODY]        </Point>
#[BODY]      </Placemark>
#[TAIL]    </Folder>
#[TAIL]  </Document>
#[TAIL]</kml>

Powyższe łącznie z przykładami można pobrać klikając tutaj.

url | Thu, 02/11/2017 03:11 | tagi: , , ,
Geotagging images with ExifTool

Note: Below it is assumed that ExifTool is used in MS Windows environment.

Download the ExifTool.exe file and place it in some directory (SomeDir)

Open the Command line (go to windows search, type cmd and the window will open)

In the command line window, change directory to SomeDir.

Geotagging photos using GPX log

Photos taken with modern smartphones are already geotagged (if Location option is On.)

If camera is unable to add geocoordinates geotagging is still possible if GPS log is available (registered with some external GPS receiver/logger).

Synchronize photos with GPS log

Download the GPX file from your GPS receiver/logger (exact procedure depends on the type of GPS receiver) to SomeDir.

Take picture of GPS receiver/or any screen displaying accurate time (PC/Smartphone screen for example).

Type in command line:

exiftool -DateTimeOriginal IMG_20171021_113121.jpg 
Date/Time Original : 2017:10:29 11:52:46

So the file was created at 11:52:46 according to DateTimeOriginal tag. Exact time is depicted. and if for example the time depicted (accurate time) is 11:50:19 CET and DateTimeOriginal is 11:52:46 CET, then the time difference is 11:32:46 - 11:50:19 = 2min 27s, ie. accurate time is obtained by substracting 2min 27s from DateTimeOriginal.

All GPS receivers internally use/record UTC time only, even if they diplay local time (cf UTC_offset). Usually the camera internally uses local time. If it is the case one should declare appropriate UTC_offset (otherwise exiftool uses system offset).

Copy all photos to SomeDir.

Type in command line:

exiftool -geosync=-02:27 '-geotime<${DateTimeOriginal}+01:00' -geotag my_gps.log SomeDir

The Geotime tag specifies the point in time for which the GPS position is calculated. -geotime<${DateTimeOriginal}+01:00 means GPS position is calculated according to DateTimeOriginal and it is CET time (+01:00 offset from UTC)

The Geosync tag specifies extra time drift, as explained above.

Generate KML file

Exiftool -p option have to be used to output files in GPX and/or KML.

exiftool -fileOrder gpsdatetime -p gpx.fmt SomeDir > SomeTripPhotos.gpx

or

exiftool -fileOrder gpsdatetime -p kml.fmt SomeDir > SomeTripPhotos.kml

KML files can be imported to Google maps and or Google earth.

Note: If such a map is not displayed locally but upload to some WWW server (http://www.gpsvisualizer.com/ in the case of GPX file for example), photos have to be uploaded to some server first and then URL to photos fixed. Exact procedure depends on which server is used for storing photos.

Example format files

gpx.fmt

#[HEAD]<?xml version="1.0" encoding="utf-8"?>
#[HEAD]<gpx version="1.0"
#[HEAD] creator="ExifTool $ExifToolVersion"
#[HEAD] xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
#[HEAD] xmlns="http://www.topografix.com/GPX/1/0"
#[HEAD] xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
#[BODY]<wpt lat="$gpslatitude#" lon="$gpslongitude#">
#[BODY]  <name>$directory/$filename</name>
#[BODY]  <ele>$gpsaltitude#</ele>
#[BODY]  <desc>$directory/$filename</desc>
#[BODY]  <link href="$directory/$filename"><type>large</type></link>
#[BODY]  <link href="$directory/$filename"><type>thumbnail</type></link>
#[BODY]  <time>$gpsdatetime</time>
#[BODY]</wpt>
#[TAIL]</gpx>

kml.fmt

#[HEAD]<?xml version="1.0" encoding="UTF-8"?>
#[HEAD]<kml xmlns="http://earth.google.com/kml/2.0">
#[HEAD]  <Document>
#[HEAD]    <name>My Photos</name>
#[HEAD]    <open>1</open>
#[HEAD]    <Style id="Photo">
#[HEAD]      <IconStyle>
#[HEAD]        <Icon>
#[HEAD]          <href>http://maps.google.com/mapfiles/kml/pal4/icon38.png</href>
#[HEAD]          <scale>1.0</scale>
#[HEAD]        </Icon>
#[HEAD]      </IconStyle>
#[HEAD]    </Style>
#[HEAD]    <Folder>
#[HEAD]      <name>Waypoints</name>
#[HEAD]      <open>0</open>
#[BODY]      <Placemark>
#[BODY]        <description><![CDATA[<br/><table><tr><td>
#[BODY]        <img src='$directory/$filename'
#[BODY]          width='$imagewidth' height='$imageheight'>
#[BODY]        </td></tr></table>]]></description>
#[BODY]        <Snippet/>
#[BODY]        <name>$filename</name>
#[BODY]        <styleUrl>#Photo</styleUrl>
#[BODY]        <Point>
#[BODY]          <altitudeMode>clampedToGround</altitudeMode>
#[BODY]          <coordinates>$gpslongitude#,$gpslatitude#,0</coordinates>
#[BODY]        </Point>
#[BODY]      </Placemark>
#[TAIL]    </Folder>
#[TAIL]  </Document>
#[TAIL]</kml>

The above with examples can be download from here

url | Tue, 31/10/2017 04:47 | tagi: , , ,
Trzecia podróż na Kaukaz

Właśnie wróciliśmy z Elką z Gruzji i Armenii, która to wycieczka była naszą trzecią w tamte rejony. Zanim więcej na temat, na razie podsumowanie naszych wyczynów w postaci pliku GPX (na pierwszej mapie czerwona kreska oznacza rok 2014, niebieska -- 2015, ciemno-zielona -- 2017): 2014-17201420152017

Ponieważ powyższe linki prowadzą do dość rachitycznego serwerka, to na wypadek gdyby ów zniknął albo przestał działać kopie plików GPX są w repozytorium na githubie tutaj

url | Wed, 11/10/2017 03:47 | tagi: , , , ,
Współrzędne geograficzne zarejestrowane kamerą Contour+

Contour+ ma GPSa i rejestruje współrzędne geograficzne, tyle że do niedawna nie bardzo wiedziałem jak (słusznie podejrzewałem że w postaci napisów aka subtitles). Wreszcie rozkminiłem jak to działa, a zmobilizowały mnie filmy zarejestrowane podczas imprezy Żuławy wKoło 2017.

Najpierw trzeba ustalić co jest w środku pliku .mov:

ffmpeg -i FILE0037.MOV
## ## ##  
Stream #0:2(eng): Subtitle: mov_text (text / 0x74786574), 1 kb/s (default)

Teraz można wyciągnąć napis znajdujący się w strumieniu (stream) 2:

ffmpeg -i FILE0037.MOV -vn -an -codec:s:0.2 srt file0037_2.srt

W pliku file0037_2.srt jest coś takiego:

692
00:11:31,000 --> 00:11:32,000
$GPRMC,061159.00,V,,,,,,,240917,,,N*7E
$GPGGA,061159.00,,,,,0,04,2.18,,,,,,*53

693
00:11:32,000 --> 00:11:33,000
$GPRMC,061200.00,A,5412.74161,N,01906.66188,E,18.465,202.51,240917,,,A*50
$GPGGA,061200.00,5412.74161,N,01906.66188,E,1,04,2.18,6.5,M,32.4,M,,*58

Czyli jest to zwykły plik napisów w formacie SRT, tj. sekwencja rekordów składających się z wierszy tekstu. Pierwszy wiersz zawiera numeru napisu (692 na przykład). Drugi wiersz określa czas wyświetlania napisu (początek --> koniec). Kolejne wiersze to tekst napisu. W przykładzie powyżej napis 692 jeszcze nie złapał fiksa, a napis 693 już tak. Współrzędne są zarejestrowane w postaci par zdań (sentences) GPRMC/GPGGA w standardzie NMEA. Do konwersji czegoś takiego na format GPX na przykład można zastosować gpsbabela

gpsbabel -i nmea -f file.srt -o GPX -F file.gpx

Ale wtedy gubi się informację z pierwszych dwóch wierszy rekordu, a jest ona niezbędna do synchronizacji obrazu ze współrzędnymi w programach nie potrafiących wykorzystać napisów wbudowanych. Chciał-nie-chciał musiałem rozpoznać NMEA i dokonać konwersji po swojemu:

$GPRMC,time,###,dd.mm,N/S,dd.mm,E/W,speed,###,date,###,###,###
$GPGGA,time,dd.mm,N/S,dd.mm,E/W,q,s,###,ele,M,###,M,###,### 

Gdzie: speed -- prędkość w węzłach czyli milach/godzinę; date -- data w formacie ddmmyy; time -- czas w formacie hhmmss.ss; dd.mm -- współrzędne geograficzne w formacie stopnieminuty.minuty tj 5412.74161 oznacza 54 stopnie 12.74161 minut a 01906.66188 oznacza 19 stopni 6.66188 minut (uwaga: szerokość/długość ma różną liczbę cyfr przed kropką dziesiętną); N/S/E/W -- kierunki geograficzne (north, south itp); q -- jakość sygnału (niezerowa wartość jest OK); s -- liczba satelitów; ele -- wysokość npm. (w metrach na szczęście w przypadku Contoura+). Zawartość pól oznaczona jako ### nas nie interesuje. Symbol M oznacza jednostkę miary (metry), z czego by wynikało, że różne odbiorniki GPS mogą zapisywać informacje o wysokości z wykorzystaniem innych jednostek miary.

Teraz banalny skrypt Perlowy zamienia SRT na format GPX dodając informacje o numerze napisu i czasie wyświetlania w postaci stosownego elementu cmt

  <trkpt lat="54.212360" lon="19.111031">
    <ele>6.500000</ele>
    <time>2017-09-24T06:12:00Z</time>
    <speed>9.499208</speed>
    <cmt>693 00:11:32,000 --> 00:11:33,000</cmt>
  </trkpt>

BTW nie ma elementu speed w specyfikacji schematu GPX, ale na przykład gpsbabel taki element wstawia i jakoś to działa. Sprawa wymaga zbadania.

Uwaga: Garmin Virb Edit nie czyta dokumentów GPX w wersji 1.0 -- musi być wersja 1.1. W praktyce oznacza to, że element gpx powinien posiadać atrybuty version oraz xmlns o następujących wartościach

<gpx version="1.1" xmlns="http://www.topografix.com/GPX/1/1">
 

Skrypt pn. cc2gpx.pl do konwersji SRT→GPX jest tutaj.

url | Tue, 26/09/2017 08:12 | tagi: , , , ,
Przed Żuławy w Koło 2017

Jutro planuję przejechać 140km biorąc udział w imprezie pn. Żuławy wKoło 2017. Niby Żuławy a profil trasy sugeruje jakieś istotne wzniesienie w okolicach 25--40km:

Uważnie przyjrzenie się liczbom (zwłaszcza na osi OY) pozwala stwierdzić, że jest to złudzenie, wynikające z różnicy w jednostkach miary obu osi (kilometry vs metry). W rzeczywistości góra tam jest symboliczna o czym można się przekonać robiąc wykres nachyleń. Żeby pozbyć się przypadkowych błędów związanych z niedokładnością pomiaru oryginalne 673 punktowe dane zostały zmienione na 111 punktowe (uśrednienie minimum 1 km) lub 62 punktowe (uśrednienie minimum 2 km).

Przy czym uśrednienie minimum $x$ oznacza obliczenie nachylenia dla najkrótszego odcinka kolejnych $n$ punktów z oryginalnego śladu GPX, który będzie dłuższy niż $x$.

Skrypty R/dane są tutaj. Oryginale ślady GPX/TCX skopiowane ze strony ŻwK są tutaj.

url | Sat, 23/09/2017 17:02 | tagi: , , ,
Wysyłanie śladów TCX na Endomondo/Strava z poprawioną wysokością

Ciąg dlaszy wpisu Rejestrowanie wysokości przez odbiorniki GPS

Ciąg dalszy bo ,,ciąg technologiczny'' FIT →TCX →GPX →srtm.py →lepszy_GPX→Endomondo/Strava pozbawia niestety przesyłany plik GPX danych, które nie są wspierane przez format GPX (takie jak tętno dla przykładu).

Problem można rozwiązać dodając do pliku TCX poprawione wysokości z pliku lepszy_GPX:

#!/usr/bin/perl
use XML::LibXML;
use XML::LibXML::XPathContext;
use Getopt::Long;

binmode(STDOUT, ":utf8");

my $Gpx_file; my $Tcx_file;

## gpx -- plik GPX z poprawionymi wysokościami
## tcx -- oryginalny plik TCX (w którym mają być poprawione wysokości)
GetOptions( "gpx=s" => \$Gpx_file, "tcx=s" => \$Tcx_file, ) ;

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

for my $file2parse ("$Gpx_file") {

  my $doc = $parser->parse_file($file2parse);
  my $root = $doc->documentElement();

  my $xc = XML::LibXML::XPathContext->new( $root );
  $xc->registerNs('gpx', 'http://www.topografix.com/GPX/1/0');

  foreach my $t ($xc->findnodes('//gpx:trkpt')) {

    my $xpe = XML::LibXML::XPathContext->new( $t );
    $xpe->registerNs('gpx', 'http://www.topografix.com/GPX/1/0');
    $node++;
    $gmttime = ($xpe->findnodes('gpx:time'))[0]->textContent();
    $altitude = ($xpe->findnodes('gpx:ele'))[0]->textContent();
    $GpxPoints{"$gmttime"} = $altitude;
  }
}

## for $e (keys %GpxPoints) { print "$e => $GpxPoints{$e}\n"; }

for my $file2parse ("$Tcx_file") {
  # http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2
  my $doc = $parser->parse_file($file2parse);

  my $root = $doc->documentElement();

  my $xc = XML::LibXML::XPathContext->new( $root );
  $xc->registerNs('tcx',
      'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2');

  foreach my $t ($xc->findnodes('//tcx:Trackpoint')) {

    my $xpe = XML::LibXML::XPathContext->new( $t );
    $xpe->registerNs('tcx',
       'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2');

    $gmttime = ($xpe->findnodes('tcx:Time'))[0]->textContent();

    foreach my $xpa ( $xpe->findnodes('tcx:AltitudeMeters') ) {
       $oldAltitude = $xpa->textContent();
       ## jeżeli istnieje w pliku GPX punkt z tym samym stemplem 
       ## czasu zmień zawartość elementu tcx:AltitudeMeters
       if (exists $GpxPoints{$gmttime} ) {
          ## replace content of tcx:AltitudeMeters
          $xpa->removeChildNodes();
          $xpa->appendText("$GpxPoints{$gmttime}");
          $changedNodesNo++;
       } else {
          ## jeżeli nie istnieje usuń cały węzeł
          my $parent = $t->parentNode;
          $parent->removeChild( $t );
          $droppedNodes .= "$gmttime;";
          $droppedNodesNo++;
       }
    }
  }

### ###
print "<?xml version='1.0' encoding='UTF-8' standalone='no' ?>\n";
print $root->toString;
### ###
}

print STDERR "Zmieniono: $changedNodesNo / Usunięto: $droppedNodesNo\n";
print STDERR "($droppedNodes)\n";

Ponieważ skrypt srtm.py nie tworzy pliku GPX zawierającego wszystkie punkty z pliku TCX (z jakiś powodów niewielka część jest pomijana); warunek exists $GpxPoints{$gmttime} sprawdza czy istnieje w pliku GPX punkt z podanym stemplem czasowym. Jeżeli istnieje zmieniana jest wysokość, jeżeli nie to punkt jest usuwany.

url | Sat, 28/05/2016 11:01 | tagi: , ,
Rejestrowanie wysokości przez odbiorniki GPS

Rejestrowanie wysokości przez odbiorniki GPS jak wiadomo odbywa się z błędem. Do tej pory mi to wisiało, ale problem stanął, gdy zachciało mi się dodać nachylenie do danych GPS (wyświetlanych jako napisy generowane autorskimi skryptami z pliku GPX/TCX), które to nachylenie nie zgadzało się wielokrotnie ze stanem faktycznym. Szczególnie tajemnicze były różnice na plus/minus 300% i więcej...

W trakcie prób rozwiązania tego problemu dowiedziałem się tego i owego na temat rejestracji wysokości. I o tym jest ten wpis. Natomiast problem z obliczaniem nachylenia został nierozwiązany. Wartości nachylenia pokazywane w trakcie jazdy przez urządzenia, takie jak Garmin Edge 500 są wiarygodnie, co by świadczyło, że zaimplementowano w nich jakiś całkiem sprytny algorytm wygładzający. Szukałem co na ten temat wie google, ale nic nie znalazłem. Próbowałem wygładzać wysokość/nachylenie w R za pomocą funkcji loess (a także średniej ruchomej) -- rezultaty były kiepskie.

Znalazłem natomiast informacje w jaki sposób można poprawić dokładność danych o wysokości, zarejestrowaną (niedokładnie) przez urządzenie GPS. Otóż można albo skorzystać z usługi Google Elevation (GE) albo użyć danych SRTM, przy czym minusem GE jest dzienny limit 2500 zapytań.

Zaczniejmy od prostszego przypadku, tj. dodania/zmiany wysokości z modelu SRTM. W tym celu pobieramy/instalujemy pakiet srtm.py. Pakiet zawiera m.in narzędzie pn. gpxelevations:

## Dodaje dane SRTM, zapisuje do pliku PLIK_SRTMS.gpx
gpxelevations -s -o PLIK.gpx -f PLIK_SRTMS.gpx
  
## Wygładza dane i zapisuje do pliku PLIK_SRTMS.gpx
gpxelevations -o 20160511.gpx -f PLIK_SRTMS.gpx

Dane SRTM można też dodać/zamienić posługując się okienkową aplikacją pn. GPSPrune, jak ktoś lubi klikać ale nie lubi wiersza poleceń.

Zakładając, że dane są w pliku GPX pobranym z urządzenia, poniższy skrypt wygeneruje plik CSV zawierający m.in. wysokość oryginalną, wysokość z modelu SRTM oraz wysokość wygładzoną:

#!/bin/bash

FILE=`basename $1 .gpx`

if [ -f "$FILE.gpx" ] ; then
  ## Dodanie lepszych wysokości
  gpxelevations -o $FILE.gpx -f ${FILE}_SRTM.gpx
  gpxelevations -s -o $FILE.gpx -f ${FILE}_SRTMS.gpx

  ## Zamiana na CSV (skrypt gpx2csv.pl zamieszczono dalej)
  gpx2csv.pl ${FILE}.gpx > ${FILE}.csv && \
  gpx2csv.pl ${FILE}_SRTM.gpx > ${FILE}_SRTM.csv && \
  gpx2csv.pl ${FILE}_SRTMS.gpx > ${FILE}_SRTMS.csv

  ## Scalenie w jeden plik CSV
  paste -d ';' ${FILE}.csv ${FILE}_SRTM.csv ${FILE}_SRTMS.csv | \
  awk -F';' ' BEGIN{ print "daytime;lat;long;ele;srtm;srtms"; };\
     {print $1 ";" $2 ";" $3 ";" $4 ";" $8 ";" $12}' > ${FILE}_ALLE.csv

else
  echo "*** USAGE: $0 PLIK.gpx ***"
fi

Teraz można porównać wysokość oryginalną, wysokość SRTM oraz wygładzoną na wykresie:

library(reshape)
require(ggplot2)

args = commandArgs(trailingOnly = TRUE);

if (length(args)==0) { stop("Podaj nazwę pliku CSV", call.=FALSE) }

fileBase <- gsub(".csv", "", args[1]);
outFile <- paste (fileBase, ".pdf", sep = "");

d <- read.csv(args[1], sep = ';',  header=T, na.string="NA");

ggplot(d, aes(x = as.POSIXct(daytime, format="%Y-%m-%dT%H:%M:%SZ"))) +
  geom_line(aes(y = ele, colour = 'zarejestrowana', group = 1), size=.5) +
  geom_line(aes(y = srtm, colour = 'srtm', group = 1), size=.5) +
  geom_line(aes(y = srtms, colour = "srtm (wygładzona)", group = 1), size=.5) +
  ylab(label="Wysokość [mnpm]") +
  xlab(label="czas") +
  labs(colour = paste("Plik:", fileBase )) +
  theme(legend.position="top") +
  theme(legend.text=element_text(size=12));

ggsave(file=outFile, width=12, height=5)

## Uruchomienie:
## Rscript gps_vs_srtm.R PLIK.csv

Oryginalne dane z urządzenia są systematycznie zaniżone.

Dodanie danych z Google Elevation jest równie proste. Poniższy skrypt -- jako przykład -- pobiera wysokość dla punktu o współrzędnych podanych jako argumenty wywołania (szerokość/długość):

use LWP::Simple; 
use JSON qw( decode_json );

my $geocodeapi =
 "https://maps.googleapis.com/maps/api/elevation/json?locations";

 ## szerokość = $ARGV[0] ; długość = $ARGV[1]
 my $url = $geocodeapi . "=$ARGV[0],$ARGV[1]";
 my $json = get($url);

 my $d_json = decode_json( $json );

 if ( $d_json->{status} eq 'OK' ) {
   $elevationG = $d_json->{results}->[0]->{elevation};
   $resolutionG = $d_json->{results}->[0]->{resolution};
   $latG = $d_json->{results}->[0]->{location}->{lat};
   $lngG = $d_json->{results}->[0]->{location}->{lng};
 }

 print "$elevationG\n";

Drugi z wykresów zawiera dane pobrane z Google Elevation Service.

Na zakończenie jeszcze skrypt zamieniający gpx na csv:

#!/usr/bin/perl
# Wykorzystanie gpx2csv.pl PLIK.gpx > PLIK.csv
#
use XML::DOM;
my $parser = new XML::DOM::Parser;

for my $file2parse (@ARGV) {
  my $doc = $parser->parsefile ($file2parse);
  for my $t ( $doc->getElementsByTagName ("trk") ) {

    for my $p ( $t->getElementsByTagName ("trkpt") ) {  $node++;
        my $latitude = $p->getAttributeNode ("lat")->getValue() ;
        my $longitude = $p->getAttributeNode ("lon")->getValue() ;

	$gmttime = $p->getElementsByTagName ("time")->
	   item(0)->getFirstChild->getNodeValue();
	
	 $altitude = $p->getElementsByTagName ("ele")->
	   item(0)->getFirstChild->getNodeValue();
	printf "%s;%s;%s;%s\n", $gmttime, $latitude, $longitude, $altitude;
       }
   }
}
url | Thu, 12/05/2016 21:17 | tagi: , ,
Import KML files to GoogleMaps

The nuisance of GoogleMaps is that it does not know GPX and only KML/GeoRSS files can be uploaded. Fortunately it is easy to convert GPX to KML with gpsbabel:

gpsbabel -i gpx -f file.gpx -x simplify,count=333  -o kml -F file.kml

If there are several files they have to be convert one-by-one and then upload to Google. It would be more comfortable to merge them first and upload it in one go rather than uploading each individually.

As I could not find how to merge serveral GPX files into one using gpsbabel (merge option in gpsbabel puts all track points from all tracks into a single track and sorts them by time stamp. Points with identical time stamps will be dropped) I worked out the following simple Perl script:

#!/usr/bin/perl
#
# Combine GPX files into one
# usage: gpxmerge file1 file2 file3 ....
#
use XML::DOM;
binmode(STDOUT, ":utf8");

my $parser = new XML::DOM::Parser;

for my $file2parse (@ARGV) {
  my $doc = $parser->parsefile ($file2parse);

  for my $w ( $doc->getElementsByTagName ("wpt") ) {  $waypoints .= $w->toString() . "\n"; }
  for my $r ( $doc->getElementsByTagName ("rte") ) {  $routes .= $r->toString() . "\n"; }
  for my $t ( $doc->getElementsByTagName ("trk") ) {  $tracks .= $t->toString() . "\n"; }
}

print "<?xml version='1.0' encoding='UTF-8' ?>
<gpx version='1.1' creator='GPXmerger'
     xmlns='http://www.topografix.com/GPX/1/1'
     xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
     xsi:schemaLocation='http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd'>\n
<author>tomasz przechlewski </author>
<email>tprzechlewski[at]acm.org</email>
<url>http://pinkaccordions.homelinux.org/Geo/gpx/</url>";

print "$waypoints\n$routes\n$tracks\n";

print "</gpx>\n";

The resulting file structure is as follows: first all waypoints, then all routes and finally all tracks. This is perfectly legal GPX file.

Now I convert GPX to KML using gpsbabel:

gpsbabel -i gpx -f file.gpx -x simplify,count=333  -o kml -F file.kml

Since gpsbabel generates pretty verbose KML files I simplify them using XSLT stylesheet (perhaps this step is superfluous):

xsltproc -o simplified.kml kml2kml.xsl file.kml

and the stylesheet looks like:


<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:kml="http://www.opengis.net/kml/2.2" >

<xsl:output method="xml"  indent='yes' />

<xsl:template match="/">

<!-- ;; http://www.jonmiles.co.uk/2007/07/using-xpaths-text-function/ ;; -->

<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
<Document>


<Folder>
  <name>Waypoints</name>

  <xsl:for-each select="//kml:Folder[kml:name/text()='Waypoints']/kml:Placemark//kml:Point">
     <Placemark>
        <!-- <name><xsl:value-of select='../kml:name'/></name> --><!-- niepotrzebne -->
	<Point>
	  <coordinates>
	    <xsl:value-of select="kml:coordinates"/>
	  </coordinates>
	</Point>
     </Placemark>
  </xsl:for-each>

</Folder>


<Folder>
  <name>Tracks</name> 
 
  <xsl:for-each select="//kml:Placemark//kml:LineString">
    
    <Placemark>
      <name>Path</name>
    
      <LineString>
	<tessellate>1</tessellate>
	<coordinates>
	
	  <xsl:value-of select="kml:coordinates"/>
	
	</coordinates>
      </LineString>
    </Placemark>
  
  </xsl:for-each>

</Folder>

</Document>
</kml>

</xsl:template>
</xsl:stylesheet>

Now KML file is ready to be imported to Google maps via Maps → My Places → Create map.

The result of example conversion can be be found here.

url | Wed, 07/03/2012 23:37 | tagi: , , , ,
Where they came from?

Google fusion tables another excercise.

Two data sets describe football players who plays in Polish t-Mobile ekstraklasa (1st division) and Pierwsza Liga (2nd division) in 2011/2012 (autumn).

To show from where the player came a straight line is drawn from a player's birthplace to club's stadium, the player plays for.

Figure 1. 1st division.

Figure 2. 2nd division.

Players from 2nd division seems to be born closer to the clubs they play for:-)

Warning: in considerable number of cases the geocoding as performed by Google maybe wrong due to poor data quality--have no time to check/correct.

Raw data are available here: div#1 and div#2.

url | Thu, 09/02/2012 10:41 | tagi: , , , , ,
Google Fusion Tables map rendering issue?

Yet another excercise, which tests Google Fusion Tables.

The data set contains--among other things--birth place for 600+ rugby players who last year take part in Rugby World Cup in New Zealand. The raw data is available here and here.

On the following diagram (cf. Figure 1) the players are mapped by column containing birthplace coordinates

Figure 1. World's concentration of top Rugby Union players.

The map do not show which player plays for which country. To show that a straight line is drawn from each player's birthplace to the country's capital, the player plays for (cf. Figure 2).

Figure 2. The origin of top Rugby Union players by federation.

Some lines look strange and the problem is particularly evident around New-Zealand-Samoa-Tonga-Fiji. For example it seems that many Samoan players were born at high ocean (cf. Figure 3).

Figure 3. The origin of top Rugby Union Samoan players.

BTW mapping Samoans one-by-one is OK (try it). The problem is when all rows are mapped together by Visualize→Map function.

Accidentaly around 600 miles to the East of New Zealand lies antipodal meridian ie. a meridian which is diametrically opposite the Greenwich meridian (a pime one). The longitude of points lying on antipodal meridian can be referenced (at least in GoogleMaps) both as -180° or 180° (ie. -36,-180° and -36,+180° refers to the same place). Perhaps it is the cause of observed errors...

url | Mon, 06/02/2012 10:54 | tagi: , , , , , ,
Trzecie ćwiczenie z Google Fusion Tables - sejm RP siódmej kadencji

Wprawdzie Sejm RP szybkimi krokami zmierza w stronę wybranego w 1936 r. Reichstagu określanego jako ,,najlepiej opłacany męski chór ma świecie'' (The best paid male chorus in the world -- określenie użyte podobno w amerykańskim czasopiśmie The Literary Digest) i być może już w kadencji ósmej ten stan ideału zostanie osiągnięty. Ale zanim to nastąpi zbierzmy dane dotyczące posłów wybranych do Sejmu 7 kadencji.

Zaczynam od ściągnięcia listy stron bibliograficznych posłów:

## Najpierw zestawienie posłów:
wget -U XX http://www.sejm.gov.pl/sejm7.nsf/poslowie.xsp?type=A -O lista-poslow.html
## Potem dla każdego posła z zestawiena:
cat lista-poslow.html | perl -e ' 
undef $/; 
$_ = <>; 
s/\n/ /g;

while ($_ =~ m@href="/sejm7.nsf/posel.xsp\?id=([0-9]+)\&amp;type=A@g ) {
    $id = $1; ## id posła
    $pos = "http://www.sejm.gov.pl//sejm7.nsf/posel.xsp?id=$id&amp;type=A";
    print STDERR $pos, "\n";
    system ('wget', '-U', 'XXX', "-O", "7_$id.html", $pos);
    sleep 3; ## --let's be a little polite--
}

Powyższe pobiera 460 stron, każda zawierająca biografię jakiegoś posła.

Z kolei poniższy skrypt wydłubuje co trzeba (data scraping) ze strony zawierających biografię pojedynczego posła (np. tego).

#!/usr/bin/perl
#
use Storable;
use Google::GeoCoder::Smart;
$geo = Google::GeoCoder::Smart->new();

# Domyślny kraj
my $GeoCodeCacheName = 'geocode.cache';
my $NewCoordinatesFetched=0; # global flag
my $kraj = 'Polska';
my $SLEEP_TIME = 3 ;
# Rok wyborów
my $baseyr = 2011; 
my ($data_day, $data_mc, $data_yr);

undef $/;
$_ = <>;
s/\n/ /g;

# Retrieve geocode cash (if exists)
# http://curiousprogrammer.wordpress.com/2011/05/11/faking-image-based-programming-in-perl/
my %hash = %{ retrieve("$GeoCodeCacheName") } if ( -f "$GeoCodeCacheName" ) ;

if (m@<h2>([^<>]+)</h2>@) { $posel = recode($1); }

# zgadnij płeć patrząc na imię (jeżeli kończy się na a->kobieta):
my @tmp_posel = split " ",  $posel;
if ( $tmp_posel[0] =~ m/a$/ ) { $sex='K' } else { $sex='M' }

# id posła:
if (m@http://www.sejm.gov.pl/sejm7.nsf/posel.xsp\?id=([0-9]+)@) {$id = $1; }

# liczba oddanych na posła głosów:
if (m@<p class="left">Liczba g\&#322;os\&oacute;w:</p>[ \n\t]*<p class="right">([^<>]+)</p>@) {
   $glosy = $1 ; }

# Data i miejsce urodzenia:</p><p class="right">29-06-1960<span>,&nbsp;</span>Gda&#324;sk</p>
if (m@Data i miejsce urodzenia:[ \n\t]*</p>[ \n\t]*<p class="right">([0-9\-]+)<span>,[ \n\t]*\&nbsp;[ \n\t]*</span>([^<>]+)</p>@ ) {
  $data = recode ($1); 
  ($data_day, $data_mc, $data_yr) = split "-", $data;
  ##print STDERR "R/M/D: $data_day, $data_mc, $data_yr\n";
  $mce = recode ($2);
}

# Zawód:
if (m@<p class="left">Zaw\&oacute;d:</p>[ \nt]*<p class="right">([^<>]+)</p>@ ) { $zawod = recode($1) ; }

if (m@klub.xsp\?klub=([^"<>]+)@ ) { $klub = recode($1); }

# Klub poselski:
if (m@Okr\&#281;g wyborczy:</p>[ \t\n]*<p class="right">([^<>]+)</p>@) {
  $okr = recode($1);
  ## Pierwszy wyraz to numer okręgu, reszta nazwa (może być wielowyrazowa)
  ($okrnr, $okrnz) =  $okr =~ m/([^ \n\t]+)[ \n\t]+(.+)/; ## -- sprawdzić czy działa --
}

# Poprawienie błędów
if ($mce =~ /Ostrowiec Świetokrzyski/) {$mce = 'Ostrowiec Świętokrzyski' }
elsif ($mce =~ /Stargard Szaczeciński/) {$mce = 'Stargard Szczeciński' ; }
elsif ($mce =~ /Białograd/ ) {$mce = 'Białogard'; }
elsif ($mce=~ /Szwajcaria/) {$mce = 'Szwajcaria,Suwałki' }
elsif ($mce=~ /Kocierz Rydzwałdzki/) { $mce= "Kocierz Rychwałdzki" }
elsif ($mce=~ /Stąporów/) { $mce= "Stąporków" }
elsif ($mce=~ /Szmotuły/) { $mce= "Szamotuły" }

# Wyjątki:
if ($posel=~ /Arkady Fiedler/ ) {$kraj = 'Wielka Brytania' }
elsif ($posel=~ /Vincent-Rostowski/) {$kraj = 'Wielka Brytania' }
elsif ($mce=~ /Umuahia/) {$kraj = 'Nigeria' }
elsif ($posel=~ /Munyama/) {$kraj ='Zambia'; }
## Imię kończy się na `A' ale to facet:
elsif ($posel=~ /Kosma Złotowski/ ) {$sex = "M"}

# Sprawdź czy data jest zawsze w formacie dd-mm-yyyy:
if ($data_yr < 1900) { print STDERR "*** $data_yr: $data for $posel ($id)\n"; }

# geokodowanie (uwaga na limit) 
my $coords = addr2coords($mce);
my $wiek =  $baseyr - $data_yr;
my $coords_okr = addr2coords($okrnz, 'r'); ($tmp_lat, $tmp_lng) = split " ", $coords;

my $kml_line = "<LineString><coordinates>$tmp_lng,$tmp_lat $coords_okr</coordinates></LineString>";

print STDERR "$id : $sex : $posel : $wiek\n";
print "$id;$sex;$posel;$data;$wiek;$mce,$kraj;$coords;$klub;$glosy;$zawod;$okrnr;$okrnz;$kml_line\n";

## Retrieve geocode cash
if ($NewCoordinatesFetched) { store(\%hash, "$GeoCodeCacheName"); }

## ## ## ## ## ##
sub recode {
  my $s = shift;

  $s =~ s/\&nbsp;/ /g; $s =~ s/\&#322;/ł/g; $s =~ s/\&oacute;/ó/g;
  $s =~ s/\&#324;/ń/g; $s =~ s/\&#321;/Ł/g; $s =~ s/\&#378;/ź/g;
  $s =~ s/\&#380;/ż/g; $s =~ s/\&#346;/Ś/g; $s =~ s/\&#379;/Ż/g;
  $s =~ s/\&#281;/ę/g; $s =~ s/\&#261;/ą/g; $s =~ s/\&#347;/ś/g;
  $s =~ s/\&#263;/ć/g; $s =~ s/[ \t\n]+/ /g;

  return $s;
}

## ## ## ## ## ##
sub addr2coords {
 my $a = shift ;
 my $r = shift || 'n';
 my ($lat, $lng) ;

 ##consult cache first
 if (exists $GeoCodeCache{"$a"} ) {
   ($lat,$lng) = split (" ", $GeoCodeCache{"$a"} );
 }
 else {
   my ($resultnum, $error, @results, $returncontent) = $geo->geocode("address" => "$a");
   $resultnum--; 
   $resultNo=$resultnum ;

   if (resultNo > 0) { print STDERR "** Location $a occured more than once! **" }
   if ($error eq 'OK') {
     $NewCoordinatesFetched=1;
     for $num(0 .. $resultnum) {
       $lat = $results[$num]{geometry}{location}{lat};
       $lng = $results[$num]{geometry}{location}{lng};
       ##print "*** LAT/LNG:$lat $lng ERROR: $error RES: $resultNo ***\n";
     }
   } else { print STDERR "** Location $a not found! due to $error **"  }
 }

 $GeoCodeCache{"$a"} = "$lat $lng"; ## store in cache
 sleep $SLEEP_TIME;

 if ($r eq 'r' ) { return "$lng,$lat"; } # w formacie KML
 else { return "$lat $lng"; }
}

Skrypt wydłubuje id posła (id), imię i nazwisko (imnz), datę urodzenia (data_ur), miejsce urodzenia (mce_ur), skrót nazwy partii do której należy (partia), liczbę oddanych na niego głosów (log), zawód (zawod), numer okręgu wyborczego (nrokregu) i nazwę okręgu (nzokregu). Do tego zgadywana jest płeć posłanki/posła oraz obliczany jest wiek w chwili wyboru jako $baseyr - $data_yr, gdzie $data_yr to rok urodzenia.

Miejsce urodzenia oraz nazwa okręgu są geokodowane. Współrzędne miejsca urodzenia są zapisywane jako zmienna wsp. Jako zmienna odleglosc zapisywana jest linia definiowana (w formacie KML) jako: współrzędne miejsce urodzenia--współrzędne okręgu wyborczego.

<LineString><coordinates>lng1,lat1 lng2,lat2</coordinates></LineString>

Zamiana kupy śmiecia (tj. wszystkich 460 plików HTML) na plik CSV zawierający wyżej opisane dane sprowadza się teraz do wykonania:

for i in 7_*html ; do perl extract.pl $i >> lista_poslow_7_kadencji.csv ; done

Jako ciekawostkę można zauważyć, że strony sejmowe zawierają 6 oczywistych błędów: Ostrowiec Świetokrzyski, Stargard Szaczeciński, Białograd, Kocierz Rydzwałdzki, Stąporów oraz Szmotuły. Ponadto wieś Szwajcaria k. Suwałk wymagała doprecyzowania. Czterech posłów urodziło się za granicą a pan Kosma jest mężczyzną i prosta reguła: jeżeli pierwsze imię kończy się na ,,a'' to poseł jest kobietą zawiodła.

Ostatecznie wynik konwersji wygląda jakoś tak (cały plik jest tutaj):

id;plec;imnz;dat_ur;wiek;mce_ur;wsp;partia;log;zawod;nrokregu;nzokregu;odleglosc
001;M;Adam Abramowicz;10-03-1961;50;Biała Podlaska,Polska;52.0324265 23.1164689;PiS;\
  12708;przedsiębiorca;7;Chełm;<LineString><coordinates>23.1164689,52.0324265 23.4711986,51.1431232</coordinates></LineString>
 ...

Teraz importuję plik jako arkusz kalkulacyjny do GoogleDocs, a następnie, na podstawie tego arkusza tworzę dokument typu FusionTable. Po wizualizacji na mapie widać parę problemów -- ktoś się urodził niespodziewanie w USA a ktoś inny za Moskwą. Na szczęście takich omyłek jest tylko kilka...

Geocoder nie poradził sobie z miejscowościami: Model/prem. Pawlak, Jarosław/posłowie: Kulesza/Golba/Kasprzak, Orla/Eugeniusz Czykwin, Koło/Roman Kotliński, Lipnica/J. Borkowski, Tarnów/A. Grad. We wszystkich przypadkach odnajdywane były miejsca poza Polską -- przykładowo Orly/k Paryża zamiast Orli. Ponadto pani poseł Józefa Hrynkiewicz została prawidłowo odnaleziona w Daniuszewie ale się okazało, że ta miejscowość leży na Białorusi a nie w Polsce. Też ręcznie poprawiłem...

Ewidentnie moduł Google::GeoCoder::Smart ustawia geocoder w trybie partial match, w którym to trybie Google intelligently handles incomplete input aka stara się być mądrzejszym od pytającego. Może w trybie full match byłoby lepiej, ale to by wymagało doczytania. Na dziś, po prostu poprawiłem ręcznie te kilka ewidentnie błędnych przypadków.

Po poprawkach wszystko -- przynajmniej na pierwszy rzut -- oka wygląda OK

Link do tabeli jest zaś tutaj.

Nawiasem mówiąc kodowanie dokumentów na stronach www.sejm.gov.pl jest co najmniej dziwaczne.

url | Sun, 29/01/2012 21:31 | tagi: , , , , , ,
Drugie ćwiczenie z Google Fusion Tables - mapa zatopionych Ubootów

Klasyczny zbiór danych pn. niemieckie łodzie podwodne z 2WWŚ wg. typu, liczby patroli, zatopionych statków i okrętów oraz -- dodane dziś -- miejsca zatopienia (źródło: www.uboat.net). W zbiorze są 1152 okręty (pomięto 14, które były eksploatowane przez Kriegsmarine ale nie były konstrukcjami niemieckimi), z tego 778 ma określoną pozycję, na której zostały zatopione (z czego kilkadziesiąt w ramach operacji DeadLight). Można zatem powiedzieć, że dane dotyczące miejsca zatopienia/zniszczenia są w miarę kompletne, bo np. Kemp (1997) podaje liczbę 784 U-bootów zniszczonych w czasie 2WWŚ (dane są różne w zależności od źródła.)

Jeżeli U-boot zaginął/przepadł bez wieści/został zniszczony, ale nie wiadomo dokładnie gdzie, to nie występuje na mapie. Operacja DeadLight to ta wielka kupa kropek ,,nad'' Irlandią....

Link do danych/mapy na serwerze Google jest tutaj.

Dane były tylko zgrubie weryfikowane, więc mogą zawierać błędy (ale ,,na oko'' jest ich niewiele)...

Literatura

Kemp Paul, U-Boats Destroyed: German Submarine Losses in World Wars, US Naval Institute Press, 1997 (isbn: 1557508593)

url | Wed, 11/01/2012 21:23 | tagi: , , , , , ,
Pierwsze próby z Google Fusion Tables

Pod tym adresem umieściłem moje ślady GPS (głównie rowerowe) z lat 2010--2012. W formacie KML bo to jedyny -- z tego co mi się wydaje -- obsługiwany przez Google Fusion Tables (GFT).

Moja procedura skopiowania danych z urządzenia GPS (zwykle jest to Garmin Legend) do GFT jest następująca:

  1. Skryptem obsługującym program gpsbabel pobieram dane z urządzenia GPS do pliku w formacie GPX.

  2. Skryptem kml2kml.sh zamieniam plik GPX otrzymany w pierwszym kroku na plik w formacie KML. Ponieważ plik ten jest ogromy (przykładowo z 60 kilowego pliku GPX gpsbabel wygenerował plik o wielkości ponad 500 kb) zostaje on za pomocą trywialnego arkusza XSLT zamieniony na znacznie mniejszą wersję uproszczoną:

    #!/bin/bash
    #
    KMLSTYLE=~/share/xml/kml2kml.xsl
    #
    # Domyslna liczba punktow na sladzie
    COUNT=99
    ## Domyslna nazwa Placemarka
    NAME=`date +%Y%m%d_%H`;
    
    while test $# -gt 0; do
      case "$1" in
    
        -name)  shift; NAME="$1";;
        -name*) NAME="`echo :$1 | sed 's/^:-name//'`";;
    
        -max)  shift; COUNT="$1";;
        -max*) COUNT="`echo :$1 | sed 's/^:-max//'`";;
        *)   FILE="$1";;
      esac
      shift
    done
    
    ## Usun rozszerzenie z nazwy pliku wejsciowego
    OUT_FILE="${FILE%.*}"
    
    TMP_FILE=/tmp/${OUT_FILE}.kml.tmp
    
    echo "Converting $FILE to ${TMP_FILE}..."
    
    if [ -f $FILE ] ; then
      ## Konwersja GPX->KML
      gpsbabel -i gpx -f $FILE -x simplify,count=$COUNT  -o kml -F $TMP_FILE
    else
      echo "*** ERROR *** File $FILE not found.... ***" ; exit 
    fi
    
    ## Konwersja do uproszczonego KMLa (KML->KML)
    echo "Converting $TMP_FILE to ${OUT_FILE}.kml with maximum $COUNT points..."
     
    echo "KML' Placemark name is: $NAME..."
    xsltproc --stringparam FileName "$NAME" -o ${OUT_FILE}.kml $KMLSTYLE $TMP_FILE
    
    echo "Done..."
    

    Zawartość arkusza XSLT zamieszczono na końcu tej notki.

  3. Do interakcji z Fusion Tables wykorzystam zmodyfikowany skrypt (nazwany ftquery.sh) znaleziony na stronach code.google.com:

    #!/bin/bash
    #
    # Copyright 2011 Google Inc. All Rights Reserved.
    # Author: arl@google.com (Anno Langen)
    # http://code.google.com/intl/pl/apis/fusiontables/docs/samples/curl.html
    # -- Modified by TP --
    MY_QUERY=$*
    
    function ClientLogin() {
      password='?1234567890?'
      email='looseheadprop1@gmail.com'
      local service=$1
      curl -s -d Email=$email -d Passwd=$password -d \
        service=$service https://www.google.com/accounts/ClientLogin | tr ' ' \n | grep Auth= | sed -e 's/Auth=//'
    }
    
    function FusionTableQuery() {
      local sql=$1
      curl -L -s -H "Authorization: GoogleLogin auth=$(ClientLogin fusiontables)" \
       --data-urlencode sql="$sql" https://www.google.com/fusiontables/api/query
    }
    
    FusionTableQuery "$MY_QUERY"
    
  4. Teraz tworzę nową tabelę (wyklikowując co trzeba w przeglądarce) MyTracks o następującej strukturze:

    ## Korzystamy z skryptu ftquery.sh
    ./ftquery.sh SHOW TABLES
    table id,name
    2590817,MyTracks
    ## Tabela MyTracks ma zatem id=2590817
    ./ftquery.sh DESCRIBE 2590817
    column id,name,type
    col4,Date,string
    col0,Start,string
    col1,Stop,string
    col2,Location,location
    col3,Description,string
    

    Kolumny zawierają odpowiednio: datę (Date), czas pierwszego wpisu na śladzie GPX (Start), czas ostatniego wpisu na śladzie GPX (Stop), ślad (Location) oraz opis (Description).

    Kolumny Tabeli w Google Fusin Tables mogą być albo napisami (STRING), liczbami (NUMBER), zawierać dane przestrzenne (LOCATION) albo czas (DATETIME). Wstępne eksperymenty z typem DATETIME wskazują, że jest z nim jakiś problem. Primo format czasu jest dość dziwaczny (np. gpsbabelowy zapis: 2012-01-07T09:19:47Z nie jest rozpoznawany). Także późniejsze filtrowanie w oparciu o kolumnę DATETIME też jakoś nie wychodzi, nawet jak zapisałem datę w formacie YYY.MM.DD, który wg. dokumentacji powinien być rozpoznawany. Nie badając sprawy dogłębnie, zmieniłem po prostu typ kolumn Date, Start, Stop na STRING.

  5. Cytując za dokumentacją GFT: In a column of type LOCATION, the value can be a string containing an address, city name, country name, or latitude/longitude pair. The string can also use KML code to specify a point, line, or polygon... (cf. SQL API: Reference ). Zatem żeby wstawić linię śladu wystarczy przesłać napis zawierający współrzędne opakowane w następujący sposób:

    <lineString><coordinates>lng,lat[,alt] lng,lat[,alt]...<coordinates></lineString>
    

    Do tego służy następujący skrypt:

    #!/bin/bash
    #
    STYLE=~/share/xml/gpxtimestamp.xsl
    MYTRACKS_TABLE_ID="2590817"
    GPX_FILE=$1
    KML_FILE="${GPX_FILE%.*}".kml
    
    ## Ustal czas od--do sladu 
    STARTTIME=`xsltproc --param Position '"First"' $STYLE $GPX_FILE`
    STOPTIME=`xsltproc --param Position '"Last"' $STYLE $GPX_FILE`
    DATE=`xsltproc --param Position '"First"' --param Mode '"Date"' $STYLE $GPX_FILE`
    
    if [ ! -f $GPX_FILE ] ; then echo "*** Error *** No GPX file: $GPX_FILE" ; exit ; fi
    if [ ! -f $KML_FILE ] ; then echo "*** Error *** No KML file: $KML_FILE" ; exit ; fi
    
    ## ## Wycinamy nastepujacym skryptem z uproszczonego pliku KML:
    TRACK=`perl -e 'undef $/; $t = <>; $t =~ m/(<coordinates>.*<\/coordinates>)/s; \
        $t = $1; $t =~ s/[ \t\n]+/ /gm; print $t;' $KML_FILE`
    TRACK="<LineString>$TRACK</LineString>"
    
    ftquery.sh "INSERT INTO $MYTRACKS_TABLE_ID (Date, Start, Stop, Location, Description) 
      VALUES ('$DATE', '$STARTTIME', '$STOPTIME', '$TRACK', '') "
    

    I działa, tj. przesyła co trzeba na moje konto Google.

Google Fusion Table map view in Chrome
GFT map view in Chrome

Google się chwali, że GFT radzi sobie z ogromnymi zbiorami danych. Mój ma -- na tem chwilem -- ponad 150 wierszy, a w każdym 99 punktów na śladzie, co daje łącznie approx 15,000 punktów do wykreślenia. Firefox/Opera coś tam wyświetla ale niewiele widać. Chrome radzi sobie najlepiej -- faktycznie da się to obejrzeć, przesuwać, powiększać... Wprawdzie nic z tych danych nie wynika, ale to już nie jest pytanie do Google.

Także filtr w połączeniu z mapą działa w Firefoksie i Operze tak sobie. Wybieram zbiór tras, np. za pomocą warunku Date < 20100901 (na tem chwilem jest to 7 wierszy) następnie klikam Visualise Map i guzik -- nic nie widać. Ale znowu w Chrome jest znacznie lepiej. Dowód w postaci zrzutu ekranu obok. Także wklejony poniżej link do mapy (pobrany przez kliknięcie w guzik Get embeddable link) daje pozytywny wynik:

Przynajmniej w mojej wersji FF (8.0).

Arkusze XSLT wykorzystane w skryptach

Zamiana pliku KML na uproszczony plik KML:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:kml="http://www.opengis.net/kml/2.2" >

<xsl:output method="xml"/>
<xsl:param name='FileName' select="'??????'"/>

<xsl:template match="/">

<kml xmlns="http://www.opengis.net/kml/2.2"
	xmlns:gx="http://www.google.com/kml/ext/2.2">
<Document>
<Placemark>

<name><xsl:value-of select="$FileName"/></name>

<MultiGeometry><LineString>
<tessellate>1</tessellate>
<coordinates>

<xsl:for-each select="//kml:Placemark//kml:LineString">

<xsl:value-of select="kml:coordinates"/>

</xsl:for-each>

</coordinates></LineString></MultiGeometry></Placemark>
</Document></kml>

</xsl:template>
</xsl:stylesheet>

Wydrukowanie daty/czasu z pliku w formacie GPX:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:gpx="http://www.topografix.com/GPX/1/0">

<xsl:output method="text"/>
<xsl:param name='Position' select="'First'"/>
<xsl:param name='Mode' select="'Time'"/>

<xsl:template match="/" >

<xsl:for-each select='//*[local-name()="trkpt"]'> 

  <xsl:choose>
    <xsl:when test="position()=1 and $Position='First' and $Mode='Time'">
      <xsl:value-of select='substring(*[local-name()="time"], 12, 8)'/> 
      <xsl:text>
</xsl:text>
    </xsl:when>
    <xsl:when test="position()=1 and $Position='First' and $Mode='Date'">
      <xsl:value-of select='translate(substring(*[local-name()="time"], 1, 10), "-", "")'/>
      <xsl:text>
</xsl:text>
    </xsl:when>
    <xsl:when test="position()=last() and $Position='Last'">
      <xsl:value-of select='substring(*[local-name()="time"], 12, 8)'/>
      <xsl:text>
</xsl:text>
    </xsl:when>
  </xsl:choose>  

</xsl:for-each>
  
</xsl:template>
</xsl:stylesheet>

Dokumentacja

  1. SQL API: Developer's Guide.

  2. SQL API: Reference .

  3. Google fusion tables cheat sheet.

url | Mon, 09/01/2012 11:51 | tagi: , , , , ,
Messtischblaetter

Tyle wiem na temat co jest na wikipedii. Mapy są dostępne np. tutaj. Weźmy jako przykład Wernersdorf, która zawiera m.in. okolice Piekła i Białej Góry.

Ściągnąłem oba wyżej wymienione pliki 2079_Wernersdorf_1939_jpg.map oraz 2079_Wernersdorf_1939.jpg.

Po to aby wprowadzić współrzędne interesujących mnie punktów do GPSa posłużyłem się programem OziExplorer (w wersji Trial). Po załadowaniu pliku 2079_Wernersdorf_1939_jpg.map w pasku na na górze okna ukazują się współrzędne punktu, na którym znajduje się wskaźnik myszy. W notacji mieszanej, tj. przykładowo 53° 54,918' oraz 18° 53,170 minut (mieszanej bo nie ma sekund tylko dziesiąte części minuty). Pewnie można to przestawić, ale ja spisałem jak jest i zamieniłem na układ 100% dziesiętny używając skryptu:

#!/usr/bin/perl
use Geo::Coordinates::DecimalDegrees;
my @dms = @ARGV ;
print STDERR "*** dms2decimal deg min sec ... deg/min/sec niekoniecznie sa liczbami calkowitymi....\n";
print STDERR "*** każde trzy liczby w linii poleceń są traktowane jako deg min sec *** \n";

while (@dms) {
  $deg = shift @dms ; $min = shift @dms ; $sec = shift @dms ;
  printf "%f %f %f =  %.6f\n", $deg, $min, $sec, dms2decimal($deg, $min, $sec);
}
## przykład:
dms2decimal.pl 53 54.918 0 18 53.170 0
> 53.000000 54.918000 0.000000 =  53.915300
> 18.000000 53.170000 0.000000 =  18.886167

Czyli 53° 54,918'/18° 53,170 to 53.915300/18.886167 w notacji dziesiętnej. Nb. wszystkie on-line serwisy do konwersji deg→dms, które oglądałem, wymagają całkowitych wartości stopni/minut/sekund, a Geo::Coordinates::DecimalDegrees nie. Inna sprawa czy to co podaje jest poprawne. Na 100% pewności nie mam ale chyba tak.

Na mapie Google wygląda to tak.

Wg mapy Wernersdorf i OziExplorera w tym miejscu jest środek mostu nad Nogatem na drodze nr 605 w Białej Górze, czyli ten punkt.

Różnica dla długości i szerokości geograficznej wynosi odpowiednio 53.915300 - 53.913498 = 0,001802 oraz 18.886167 - 18.883739 = 0,002428.

W odpowiedź na pytanie czy ja coś poplątałem czy ktoś inny na razie nie będę się wgłębiał. Zamiast tego to co odczytam w OziExplorerze skoryguję (na razie) za pomocą wyżej wyznaczonych poprawek.

OziExplorer się instaluje w Fedorze, się uruchamia za pomocą wine, ale beznadziejnie kończy pracę komunikatem o brakującej pamięci...

url | Wed, 27/04/2011 19:02 | tagi: , , , ,
Zmiana wyglądu ikony w Google maps

W bibliotece, z której korzystam do wizualizowania plików GPX na google maps, do wyświetlenia punktu (czyli elementu wpt) wykorzystywana jest funkcja GPXParser.prototype.CreateMarker:

GPXParser.prototype.CreateMarker = function(point)
{
        var lon = parseFloat(point.getAttribute("lon"));
        var lat = parseFloat(point.getAttribute("lat"));
  // ... ... ...
        var marker = new GMarker(new GLatLng(lat,lon));
  // ... ... ...
}

W powyższym symbol // ... ... ... oznacza pominięte fragmenty funkcji.

Wszystkie ikony dla elementów wpt są oznaczone standardową dużą czerwoną pinezką. Ja chcę, żeby moja pinezka ,,reagowała'' na obecność elementu type, który według schematu GXP, określa typ punktu. Przykładowo miejsce odwiedzone będę oznaczał jako:

<wpt lat="54.071729" lon="18.446817"><ele/>
 <name>Skarszewy</name>
 <type>visited</type>
 <extensions><html>Skarszewy</html></extensions></wpt>

Tak oznaczone miejsca mają być wyróżniane za pomocą niebieskiej ikony. Pozostałe będą oznaczane czerwoną:

GPXParser.prototype.CreateMarker = function(point)
{
        var lon = parseFloat(point.getAttribute("lon"));
        var lat = parseFloat(point.getAttribute("lat"));

        var wpType = point.getElementsByTagName("type")[0] ; // czy istnieje element type?
        var wpTypeTxt = "?";

	if ( wpType ) { wpTypeTxt = wpType.firstChild.nodeValue; } // jeżeli tak przypisz wartość

  // ... ... ...
        // zmiana wyglądu, skopiowane z: http://kody.wig.pl/tag/gmarker/
        var cIcon = new GIcon();

        // Ikony do pobrania z http://sites.google.com/site/gmapicons/
        // umieszczam w katalogu '/icons_/'
        if ( wpTypeTxt == "visited" ) { cIcon.image = '/icons_/mm_20_blue.png'; }
        else { cIcon.image = '/icons_/mm_20_red.png'; }

        cIcon.shadow = "/icons_/mm_20_shadow.png";
        var marker = new GMarker(new GLatLng(lat,lon), cIcon );

        cIcon.iconSize = new GSize(12, 20);
        cIcon.shadowSize = new GSize(22, 20);
        cIcon.iconAnchor = new GPoint(6, 20);
        cIcon.infoWindowAnchor = new GPoint(5, 1);

  // ... ... ...
}

Wynik można obejrzeć tutaj.

url | Tue, 19/04/2011 21:07 | tagi: , , ,
Navin minihomer GPS receiver/logger
Navin GPS
Navin minihomer

No maps or advanced features are supported but thanks to 1 inch display screen it can shows direction (electronic compass) and distance to up to 5 different targets. Targets (POIs) can be marked, manually entered or uploaded with appropriate software. (IMHO manual entry of POI requires at least Kristian Zimerman's fingers---for mere human it is simply impossible to remember and perform all key clicks in appropriate sequence...)

The producer recommends ntrip Travel Management Shareware. Two main features of ntrip are: -- importing logged GPS data, -- setting up to 5 locations by clicking them on the Google map. Ntrip is Windows only (perhaps it works with wine---I didn't try) shareware and the license fee is 20 USD.

Brand new minihomer has logging capability disabled. On first connection to ntrip it is enabled. One can stop/start logging manually with mark key.

As I do not use MSW (MS Windows) and don't like shareware applications I have been searching for some free replacement for ntrip starting with an obvious one, namely gpsbabel.

Latest version of Gpsbabel (1.4.2) supports skytraq venus based loggers (Venus 5 and 6; for 130 PLN at allegro.pl) but the support for minihomer is limited to dowloading the GPS tracks only:

gpsbabel -i skytraq -f /dev/ttyUSB0 -o gpx -F out.gpx
## if the above do not works thy add baud=0 ; erase clears all device's memory
gpsbabel -i skytraq,erase,baud=0 -f /dev/ttyUSB0 -o gpx -F out.gpx

Alternatively one can use skytraq-datalogger:

## get information about software version and configuration
skytraq-datalogger --info --device /dev/ttyS0

## change configuration of the data logger (do not works with miniHomer):
skytraq-datalogger --enable-log --set-config

## dumping memory in GPX (works):
skytraq-datalogger --dump > out.gpx

Finally a guy called Josef Reisinger adds Minihomer support to gpsbabel but it has not been integrated (yet?) to official distribution. However the `hacked' gpsbabel can be dowloaded from www.ohloh.net:

cvs -d :pserver:anonymous:@gpsbabel.cvs.sourceforge.net:/cvsroot/gpsbabel co gpsbabel
cd gpsbabel
./configure
make
## change gpsbabel to something else:
mv gpsbabel gpsbabel_minihomer

To set location for ``Home'' icon one can issue a command similar to the following example:

## =55.44:18.55:45 is latitute:longitude:altitude ; altitude is optional
## -o gpx -F out.haha is suprefluous, perhaps -F - will suffice:
gpsbabel_minihomer -i minihomer,Home=55.44:18.55:45 -f /dev/ttyUSB0 -o gpx -F out.haha

Other icons include ``Car'', ``Boat'', ``Heart'' and ``Bar''. The track can be dowloaded as with ordinary version of gpsbabel of course. It seems to be impossible to configure the device however (disabling/enabling logging, setting logging period etc...) so enabling logging can be done---as far as I know---with ntrip only.

Dopisane 24 marca 2011: Się okazało, że połączenie gpsbabel/minihomer jest straszliwie zależne od użytego kabla USB. Kto by pomyślał. Mi działa z kablem oznaczonym jako Belkin Pro Series USB 2.0 [...] E237114 CSA 219561 Z innymi nie działa... Trudno powiedzieć czy to jest oryginalny dołączony do urządzenia, bo różnych kabelków USB mam już z kilkanaście...

Dopisane 28 marca 2011: Ponieważ ciągle nie działa poprawnie wykonałem następujące testy:

## działa:
gpsbabel -i skytraq,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110326.gpx
## nie działa:
gpsbabel -i skytraq,erase,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110326-e.gpx
skytraq: Can't find skytraq device on '/dev/ttyUSB0'
## jeszcze raz polecenie nr1 teraz też nie działa:
gpsbabel -i skytraq,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110326p.gpx
skytraq: Can't find skytraq device on '/dev/ttyUSB0'
## wyjęcie USB/wsadzenie, powtórka, działa:
gpsbabel -i skytraq,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110326p.gpx
## i jeszcze raz, działa:
gpsbabel -i skytraq,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110326pp.gpx
## teraz próba zmodyfikowanym babelem:
miniHomer_gpsbabel -i minihomer,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110324m.gpx
skytraq: Can't find skytraq device on '/dev/ttyUSB0'
## wyjęcie USB/wsadzenie:
miniHomer_gpsbabel -i minihomer,baud=0 -f /dev/ttyUSB0 -o gpx -F 20110324m.gpx
Naruszenie ochrony pamięci

##
## działa natomiast:
miniHomer_gpsbabel -i minihomer,Bar=55.44:18.55:45 -f /dev/ttyUSB0 -o gpx -F -

Reasumując ślad można zgrywać ,,klasycznym'' babelem. Zmodyfikowany ma coś spieprzone, ale działa w trybie ustawiania POI, czego nie potrafi ,,klasyczny'' z kolei. Żaden nie obsługuje polecenia erase. To nie jest wszakże aż tak wielki problem bo pamięć po zapełnieniu jest nadpisywana (chyba:-).

Poza tym w komputerze kol. B. nie działało w ogóle. Pomogło wpisanie w google frazy: gpsbabel+hangs. Należy wykonać przed uruchomieniem gpsbabela [ale po podłączeniu urządzenia] następujące tajemnicze polecenie:

stty -F /dev/ttyUSB0 clocal

co jest opisane tutaj albo tutaj. Z opisu zaś wynika, że błąd dotyczy nowych wersji Ubuntu lub nawet kernela per se (nie chciało mi się czytać, bo ja mam ciągle Fedorę acht i jest OK!).

url | Sun, 06/03/2011 14:23 | tagi: , , , , , ,
Zdjęcia z flickr.com na googlemaps

Flickr2Map to usługa, dzięki której użytkownicy flickr.com mogą wyświetlić geotagowane zdjęcia m.in. na google maps. Przykład:

http://www.robogeo.com/Flickr2Map?referer=http://www.flickr.com/photos/tprzechlewski/3087054865/&type=map&view=satellite

Żeby za dużo nie musieć wpisywać, zrobiłem skrót do usługi.

url | Sun, 28/11/2010 14:31 | tagi: , , , ,
Sony Ericsson Elm J10i2

Nie to żebym się chwalił ale od pewnego czasu posiadam. Ten telefon ma GPS...

Jak się zdjęcia robi, to można ustawić aparat tak, żeby zdjęcia były geo-tagowane, tj. żeby były dodawane współrzędne geograficzne. Ale jest pewien myk. Mianowicie jeżeli GPS nie będzie włączony -- cokolwiek to znaczy -- to geo-pozycja zdjęcia będzie wyznaczona tylko w oparciu o numer komórki (cell). Wygląda to tak:

$ exiftool -X -xmp:all dsc00001.jpg

<rdf:Description rdf:about='dsc00001.jpg'
  xmlns:et='http://ns.exiftool.ca/1.0/' et:toolkit='Image::ExifTool 8.06'
  xmlns:XMP-rdf='http://ns.exiftool.ca/XMP/XMP-rdf/1.0/'
  xmlns:XMP-cell='http://ns.exiftool.ca/XMP/XMP-cell/1.0/'>
 <XMP-rdf:About> </XMP-rdf:About>
 <XMP-cell:Mcc>260</XMP-cell:Mcc>
 <XMP-cell:Mnc>3</XMP-cell:Mnc>
 <XMP-cell:Lac>30203</XMP-cell:Lac>
 <XMP-cell:Cellid>32930160</XMP-cell:Cellid>
 <XMP-cell:TagR>5</XMP-cell:TagR>
 <XMP-cell:Cgi>260-3-30203-32930160</XMP-cell:Cgi>
</rdf:Description>
</rdf:RDF>

Można powyższe zamienić na współrzędne wpisując:

http://www.opencellid.org/cell/get?mcc=260&mnc=3&cellid=32930160&lac=30203

Ale wyniki jest tylko przybliżony...

Teraz w instrukcji telefonu jest, że GPS sam się wyłącza jak nie jest potrzebny. A jak go włączyć na zicher? Zainstalowałem TrekBuddy i używam go jako loggera. Efekt uboczny jest taki, że GPS działa non-stop [zapewne da się to także osiągnąć inaczej].

Zdjęcia zrobione w takim trybie są już oznaczone precyzyjnie:

2.2.0.0
 North
 54 deg 26' 22.62"
 East
 18 deg 33' 2.50"
 Above Sea Level
 41 m
 WGS-84
 41 m Above Sea Level
 54 deg 26' 22.62" N
 18 deg 33' 2.50" E
 54 deg 26' 22.62" N, 18 deg 33' 2.50" E
]]>

Natomiast zapisywany przez TrekBuddy ślad w formacie NMEA jest do kitu. Na blogu blog.4zal.net znalazłem informację, że w istocie: ,,osoby chcące skorzystać z logowania, a posiadające GPS wbudowany w komórkę powinny uważać na logowanie w formacie NMEA. W przypadku niektórych telefonów (np. Sony Ericsson) tak wygenerowane dane będą niekompletne''.

Zatem przestawiłem format na GPX i dziś zrobiłem pierwszy test. Poszedłem do Oliwy [na wywiadówkę] i z powrotem z telefonem i Garminem w kieszeni.

Ślady są tutaj. Niebieski to Elm, czerwony -- Legend. Hmm....

Dopisane 11 stycznia 2011: Dłuższe hmmmmmm jest takie, że każde zdjęcie kosztuje 25 gr. Wybierając opcję geotagowania wyświetla się komunikat, że to może kosztować. No więc nie tylko może ale kosztuje (Orange). Zresztą jak ustaliliśmy wyżej pozycja ustalana domyślnie przez telefon jest taka sobie dokładna, a ponieważ Elm ma wbudowany odbiornik GPS, to przecież teoretycznie mógłby wstawiać współrzędne bez łączenia się z centralą... Ale nie ma tak, trzeba płacić nawet jak się nie korzysta -- jak się wyłączy opcję dodawania współrzędnych, to zdjęcia nie są geotagowane. Jednym słowem kolejna wielka lipa...

Nawet specjalnie tego nie żałuję, bo marny ten GPS -- jak startuje 5 minut to jest szybko.

url | Thu, 04/11/2010 22:18 | tagi: , , ,
Gpsbabel i /dev/ttyUSB0 w fedora 11

Poprawne działanie mojego skryptu My-get-GPX.sh, który służy do pobierania danych w formacie GPX z urządzeń GPS wymaga w systemie Fedora 11 ręcznego zainstalowania pakietu perla Geo::Distance oraz dopasowanie systemu według zaleceń z wiki.openstreetmap.org. Mianowicie w pliku /etc/modprobe.d/blacklist.conf umieszczamy [Nb. z mojego opisu instalacji MapSource pod wine, wynika że może się to gryźć z MapSource. Na razie się nie gryzie bo ww. MS mam na komputerze ze starszą wersją Fedory....]:

blacklist garmin_gps

oraz zmieniamy uprawnienia do urządzenia (bez tego ściągać dane może tylko root), dodając do katalogu /etc/udev/rules.d/ plik 51-garmin.rules zawierający:

SYSFS{idVendor}=="091e", SYSFS{idProduct}=="0003", MODE="0666"

Teraz należy nowe reguły załadować:

sudo udevadm control --reload-rules

Uwaga: gpsbabel widzi urządzenie jako usb:0 a nie jak poprzednio, w starszej wersji Fedory /dev/ttyUSB0. W nowej fedorze przestał też działać -- zapewne z uwagi na havoc z urządzeniami -- bt747. Szukając zamiennika doczytałem, że gpsbabel też potrafi ściągnąć dane z i-Blue 747:

gpsbabel -t -w -i mtk,erase -f /dev/ttyUSB0 -o gpx -F out.gpx

Opcja erase powoduje wyczyszczenie zawartości pamięci... Oprócz pliku .gpx dane są ściągane w formacie binarnym i zapisywane do pliku data.bin. Gpsbabel nie umie m.in. manipulować ustawieniami loggera, ale nie jest to dla mnie aż tak wielka strata, bo nigdy nic nie ustawiałem... Ciekawostką jest, że dla i-Blue 747 działa /dev/ttyUSB0 a w przypadku Legenda wspomniane usb:0. Trochę to dziwne, albo znowu czegoś nie doczytałem...

Anyway, skrypt My-get-GPX.sh obsługuje teraz zarówno Garmin Legend jak i Blue 747. Dodałem też opcję -publish, działającą w ten sposób, że po pobraniu pliku GPX jest wycinany ślad ,,z dziś'' (tj. od 6.00 do 23.00 dla bieżącej daty), który to ślad jest następnie wysyłany w odpowiednie miejsce na serwerze (np. tutaj). Reasumując: po przyjechaniu z wycieczki rowerowej wystarczy wsadzić kabel USB, włączyć urządzenie i wpisać:

My-get-GPX.sh

A reszta zrobi się sama...

url | Sun, 22/08/2010 17:46 | tagi: , , ,
Gmap-pedometer i ograniczenia Legenda

Intensywne używanie http://www.gmap-pedometer.com/ spowodowało, iż dotarłem do granic możliwości Legenda, o których NB. do niedawna nie miałem pojęcia. Otóż Legend może przechować do 1000 punktów (waypoints), 20 śladów (tracks) oraz do 50 tras (routes), każda o długości do 250 punktów... Ponieważ punkty trasy liczą się do puli punktów, 5 tras 200 punktowych zawala pamięć ,,na amen''. Co zresztą niedawno miało miejsce...

Usunięcie wszystkich punktów i wszystkich tras z Garmina jest bardzo łatwe. Tyle, że chciałbym niektóre zachować... Można wprawdzie usuwać punkty według związanych z nimi symboli, ale to by wymagało porządku przy ich definiowaniu. A ja, póki co, mam w tym względzie bałagan. Zatem:

  1. Zgrałem zawartość pamięci Garmina do pliku. Wywaliłem wszystkie trasy i ślady oraz związane z nimi punkty (są one łatwo rozpoznawalne po nic nie mówiącej nazwie typu Turn 233 albo GRTP-202). Pozostawiłem pozostałe punkty. (Wywalając trasy trzeba usunąć wszystkie, bo nie wiadomo jaki punkt należy do jakiej trasy -- tak jest szybciej i prościej.)

  2. Wyczyściłem pamięć urządzenia. W tym celu w menu głównym należy wybrać ikonę Waypoints→przycisk menu/find→Delete. Teraz wybrać All Symbols i potwierdzić. Uwaga: przycisk menu/find jest po lewej na dole pod przyciskami góra/dół.

  3. Załadowałem z powrotem plik GPX (nazwałem go LEGEND_MEM.gpx):

    gpsbabel -r -w -t -i gpx -f LEGEND_MEM.gpx -o garmin -F /dev/ttyUSB0
    

Plik LEGEND_MEM.gpx zawiera moją bazę podstawowych punktów i tras (jeżeli takowe są)... Resztę będę ładował w miarę potrzeby, a jak się znowu zapełni pamięć Legenda, to powtórzę opisaną wyżej procedurę... Przy okazji 225 jako górny limit punktów na trasie wygląda na zbyt dużo. Spróbuję 100 punktów, wtedy zmieści się więcej tras....

url | Fri, 25/06/2010 22:04 | tagi: , , , , ,