W zeszłym roku wziąłem udział w imprezie kolarsko-rekreacyjnej pn. Żuławy w Koło, a teraz zapisałem się na Kociewie Kołem, która ma się odbyć 9 września. Ta sama firma organizuje jak się łatwo domyśleć.
Żeby nie jechać w ciemno pobrałem stosowne dane ze strony organizatora, zamieniłem je na plik CSV i policzyłem różne statystyki. W 2016 średnia prędkość na najdłuższym dystancie (170 km) wyniosła na przykład 27,05 km/h. Rok później (dystans 155 km) było to 26,69 km/h. Czyli sporo, bo na płaskiej i krótszej trasie Żuławy w Koło było dla przykładu w 2016 roku 25,47 km/h, a w 2017 26,23 km/h. Więcej szczegółów na wykresach pudełkowych obok.
Ściągnąłem też w środę listę uczestników, których okazało się jest 719, w tym z Gdańska 332, z Gdyni 107, a tak w ogóle to ze 120 różnych miejscowości. Za pomocą Google Fusion Tables można pokazać listę na mapie. Żeby kropki z tej samej miejscowości się nie nakładały na siebie zastosowałem losowe `drganie' (jitter) wg. algorytmu:
### Jitter w kole o średnicy $r $factorJ = 0.00001; ## ustalone heurystycznie $sd = sqrt($factorJ * $N); # $N liczba kropek dla miejscowosci, tj dla GDA 332 $r = $sd * sqrt(rand()); $theta = rand() * 2 * $pi; $rand_lat = $lat + $r * cos($theta); $rand_lon = $lon + $r * sin($theta); ### Jitter w prostokącie o boku $r $rand_lat = $lat + rand($sd); $rand_lon = $lon + rand($sd);
Rezultat jak na obrazku poniżej, albo tutaj.
Lewy obrazek to mapa bez `jittera' a prawy z zastosowanym `jitterem'.
Bo zmieniła się licencja na korzystanie (z Google Maps API):
Google has made significant changes to the way they allow the use of their Google Maps Platform. Google maintains its Google Map API will still be free to a certain limit and it provides free $200 monthly recurring credit to every user. Google requires all user to have a billing account. They must be willing to submit credit card details, so excess is charged pro rata.
Więcej jest tutaj.
W rezultacie tych zmian na mapach Google, które wykorzystują poprzez API (konkretnie korzystają z Maps JS API) pojawiło się okienko z komunikatem Ta strona nie może poprawnie wczytać Map Google. Sama wyświetlona mapa jest zaś na szaro i ma powtórzony znak wodny For development purposes only.
No kicha niewątpliwie. Na razie nie wiem co robić, bo rejestrować kartę do moich hobbystycznych projektów to mi się nie chce. Rozważam wykorzystanie czegoś w zamian.
Oprócz Google Maps JS API korzystałem też z Google Geocoding API. No i ta usługa też przestała działać: OVER_QUERY_LIMIT dostaję w odpowiedzi. Tutaj też rozważam wykorzystanie czegoś w zamian, a nawet rozważyłem i spróbowałem: darmowy serwis MapQuest. BTW nie wiemy czy to jest najlepszy zamiennik ale spróbować trzeba.
Piszą (cf. https://developer.mapquest.com/documentation/open/geocoding-api/
), że:
The Open Geocoding API is similar
to our MapQuest Geocoding API, but instead relies solely
on data contributed to OpenStreetMap.
Zapytania mogą mieć postać metod GET
lub POST
.
Jest jeszcze coś co się nazywa Batch Geocode
, które to coś
pozwala na geokodowania do 100 adresów na raz.
Szczegółowa dokumentacja jest
tutaj.
Posługując się metodą GET korzysta się bardzo prosto:
http://open.mapquestapi.com/geocoding/v1/address?key=APIKEY&location=ADDRESS
Użyteczne parametry:
boundingBox=UppLLat,UppLLon,LowRLat,LowRLon
(górny lewy/dolny prawy),
maxResults
,
outFormat
(json,xml albo csv).
Przykłady:
http://open.mapquestapi.com/geocoding/v1/address?key=KLUCZ&location=Sopot,PL&maxResults=10 ## BB województwa pomorskiego 54.83572969999 19.6489837 53.49067179999999 16.699129 http://open.mapquestapi.com/geocoding/v1/address?key=KLUCZ&location=Sopot,PL&\ boundingBox=54.835729,19.648983,53.490671,16.699129
Żeby było łatwiej powyższe zaimplementowałem w prostym skrypcie Perlowym:
#!/usr/bin/perl # use strict; use LWP::Simple; # from CPAN use JSON qw( decode_json ); # from CPAN use Getopt::Long; #use Encode; use utf8; binmode(STDOUT, ":utf8"); my $format = "json"; # xml | csv (not implemented) my $myAPIkey = 'APIKEY'; ## Klucz od MapQuesta (trzeba się zarejestrować) my $geocodeapi = "http://open.mapquestapi.com/geocoding/v1/address?key=$myAPIkey&location="; my $addrLine = ''; my $logFile = ''; my $boundingBox = ''; my $country = 'PL'; my $maxResults = 1; ## podaj tylko pierwszy my $USAGE="USAGE: $0 [-a ADDRESS] [-log FILENAME] [-country CODE] [-bb SWlat,SWlng|NElat,NElng] \n"; GetOptions( "a=s" => \$addrLine, "log=s" => \$logFile, "country=s" => \$country, "bb=s" => \$boundingBox, "max=i" => \$maxResults ) ; if ($logFile) { open LOG, ">>$logFile" || die "Cannot log to $logFile\n" ; print STDERR "*** Logging JSON to $logFile ***\n" ; } else { print STDERR "*** Logging JSON OFF ***\n"; } unless ($addrLine ) { print STDERR "$USAGE"; exit 0; } my ($lat, $lng, $quality, $lname) = getLatLong($addrLine, $boundingBox); printf "Lat/Lng/Name/Quality: %s %s %s %s\n", $lat, $lng, $lname, $quality; if ($logFile) { close (LOG) ; } ### ### ### ### ### ### ### ### sub getLatLong($){ my $address = shift; my $bb = shift; my $url = $geocodeapi . "$address,$country" ; # if ($boundingBox ) { $bb =~ s/[\|,]/,/g; $url .= "&boundingBox=$bb" ; } if ($maxResults ) { $url .= "&maxResults=$maxResults" ; } print STDERR "\n*** [URL: $url] ***\n"; my $json = get($url); if ($logFile) { print LOG "##_comment_:\n##_comment_:$url\n$json\n"; } my $d_json = decode_json( $json ); #if ( $d_json->{statuscode} eq '0' ) { my $loc_name = $d_json->{results}->[0]->{providedLocation}->{location}; my $lat = $d_json->{results}->[0]->{locations}->[0]->{latLng}->{lat}; my $lng = $d_json->{results}->[0]->{locations}->[0]->{latLng}->{lng}; my $quality = $d_json->{results}->[0]->{locations}->[0]->{geocodeQuality}; return ($lat, $lng, $quality, $loc_name ); #} else { return ( $d_json->{statuscode} ) } } ## ///
Teraz wystarczy na przykład:
geocodeCoder1.pl -a Sopot -bb 54.835729,19.648983,53.490671,16.6991