Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | Housekeeping: Formatting, variable naming consistency, etc
- Get rid of the mixed up mess of CamelCase, underscores, prefixes, |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk | origin/master |
Files: | files | file ages | folders |
SHA3-256: |
301914a6a181661f7bdf5bb0ad9e430a |
User & Date: | base@atomicules.co.uk 2014-12-05 20:43:53 |
2014-12-05
| ||
23:51 |
Calculate wind-type percentages and use to determine win/lose
In other words: done for now. Those are pretty much the changes I | |
20:43 |
Housekeeping: Formatting, variable naming consistency, etc
- Get rid of the mixed up mess of CamelCase, underscores, prefixes, | |
2014-11-30
| ||
11:02 |
Extend functionality to sum up all wind directions
by mapping. Need to tidy up formatting and make it consistent and sort out the | |
Changes to headsilose.erl.
1 2 3 4 5 6 7 8 9 10 | -module(headsilose). -export([get_locations/0, get_locations/1, get_weather/1, headsilose/1]). -include_lib("xmerl/include/xmerl.hrl"). -import(weather_types, [weather_type/1]). -import(polyline, [decode/1]). -import(osrm, [read_route/0]). %Supply a direction and location and work out if head wind or not %For now "know the location id" upfront, but ideally need to search for it at some point or present a choice. | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | -module(headsilose). -export([get_locations/0, get_locations/1, get_weather/1, headsilose/1]). -include_lib("xmerl/include/xmerl.hrl"). -import(weather_types, [weather_type/1]). -import(polyline, [decode/1]). -import(osrm, [read_route/0]). %Supply a direction and location and work out if head wind or not %For now "know the location id" upfront, but ideally need to search for it at some point or present a choice. %Initially based on: http://pragdave.pragprog.com/pragdave/2007/04/a_first_erlang_.html -define(BASE_URL, "http://datapoint.metoffice.gov.uk/public/data/"). -define(WXFCS_SITELIST, "val/wxfcs/all/xml/sitelist"). |
︙ | ︙ | |||
69 70 71 72 73 74 75 | try { ok, {_Status, _Headers, Body }} = httpc:request(get, {URL, []}, [{timeout, timer:seconds(10)}], []), { Xml, _Rest } = xmerl_scan:string(Body), [ [ #xmlAttribute{value=Direction} ], [ #xmlAttribute{value=Speed} ], [ #xmlAttribute{value=Gust} ], [ #xmlAttribute{value=Weather} ], | | > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | try { ok, {_Status, _Headers, Body }} = httpc:request(get, {URL, []}, [{timeout, timer:seconds(10)}], []), { Xml, _Rest } = xmerl_scan:string(Body), [ [ #xmlAttribute{value=Direction} ], [ #xmlAttribute{value=Speed} ], [ #xmlAttribute{value=Gust} ], [ #xmlAttribute{value=Weather} ], [ #xmlAttribute{value=Temperature} ] ] = lists:map( fun(X) -> xmerl_xpath:string("//Period[@value='" ++ Date_formatted ++ "']/Rep[.='" ++ Rep ++ "']/@"++X, Xml) end, ["D", "S", "G", "W", "T"]), {Direction, Speed, Gust, Weather, Temperature} catch _:Reason -> io:format("API Might be down~n"), |
︙ | ︙ | |||
101 102 103 104 105 106 107 | Rep = "360", {format_date(find_next_day(Date)), Rep}. format_date(Date_to_format) -> {{Year, Month, Day}, {_Hours, _Minutes, _Seconds}} = Date_to_format, %Thanks to: http://stackoverflow.com/a/7599506/208793 | | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | Rep = "360", {format_date(find_next_day(Date)), Rep}. format_date(Date_to_format) -> {{Year, Month, Day}, {_Hours, _Minutes, _Seconds}} = Date_to_format, %Thanks to: http://stackoverflow.com/a/7599506/208793 Date_as_string = io_lib:format("~4..0w-~2..0w-~2..0wZ", [Year, Month, Day]), lists:flatten(Date_as_string). nth_wrap(N, List) -> Rem = N rem (length(List)), if Rem > 0 -> lists:nth(Rem, List); Rem =:= 0 -> |
︙ | ︙ | |||
127 128 129 130 131 132 133 | maybe_quit() -> Args = init:get_arguments(), Found = lists:keyfind(noshell, 1, Args), if Found =:= false -> dont_quit; | | > | | | | | > | > | > | | | | | | | > | > | > | | | | > | > | | > | > > | | > > > > > > | < | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | maybe_quit() -> Args = init:get_arguments(), Found = lists:keyfind(noshell, 1, Args), if Found =:= false -> dont_quit; true -> init:stop() end. build_list_of_wind_directions(Wind_direction) -> %There is only one wind direction, but can build groups of directions that will count as head, side and tail winds Compass = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"], %Quicker dirtier(?) way to do below would be: http://stackoverflow.com/a/6762191/208793 Index = length(lists:takewhile(fun(X) -> X =/= Wind_direction end, Compass))+1, %Since heading is to direction and winds are from, opposite is -2 to +2, or to make it easier to wrap, +14 +18 %Since heading is to direction and winds are from, sidewinds are -5 to -3 and +3 to +5, or to make it easier to wrap, +14 +18 %And so on Headwind_list = lists:seq(14,18), Sidewind_list = lists:seq(3,5)++lists:seq(11,13), Tailwind_list = lists:seq(6,10), lists:map( fun(Wind_list) -> lists:map( fun(X) -> nth_wrap(Index+X, Compass) end, Wind_list) end, [Headwind_list, Sidewind_list, Tailwind_list]). convert_lats_longs_to_distance_heading([_Head1 | [ _Head2 | Rest]]) -> %All co-ords are diff, so just ignore first two convert_lats_longs_to_distance_heading_(Rest, []). convert_lats_longs_to_distance_heading_([Lat | [Lon | Rest]], Distance_headings_list) -> %Want to map through the list convert co-ords to distance and heading Distance = math:sqrt(math:pow(Lat,2) + math:pow(Lon,2)), Heading_signed = math:atan2(Lon, Lat), %Need to convert heading into a 2π value Heading = if Heading_signed < 0 -> Heading_signed + 2*math:pi(); true -> Heading_signed end, Compass_direction = get_compass_direction_for(Heading), convert_lats_longs_to_distance_heading_(Rest, [{Distance, Compass_direction}]++Distance_headings_list); convert_lats_longs_to_distance_heading_([], Distance_headings_list) -> lists:reverse(Distance_headings_list). get_compass_direction_for(Heading) -> %In a way this is a waste of time as could just do headwind, etc based on angles, but since already have some code, why not? Segment = 2*math:pi()/16, Segments = erlang:round(Heading/Segment), Compass = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"], lists:nth(Segments, Compass). head_side_or_tail_wind(Direction, [Headwinds, Sidewinds, Tailwinds]) -> [Headwind, Sidewind, Tailwind] = lists:map( fun(Winds) -> lists:member(Direction, Winds) end, [Headwinds, Sidewinds, Tailwinds]), if Headwind -> headwind; Sidewind -> sidewind; Tailwind -> tailwind end. %Something like that? headsilose(Location) -> {Direction, Speed, Gust, Weather, Temperature} = get_weather(Location), Weather_type = weather_types:weather_type(erlang:list_to_integer(Weather)), [Headwinds, Sidewinds, Tailwinds] = build_list_of_wind_directions(Direction), %Ok so now map this list over (like previous function) the headings list to get list of head, side, tail and distances? {_Checksum, Polyline} = osrm:read_route(), Polyline_decoded = polyline:decode(Polyline), Distances_and_compass_list = convert_lats_longs_to_distance_heading(Polyline_decoded), %If now have a set of co-ords need to figure out distances and directions Distances_and_wind_type_list = lists:map( fun({Distance, Compass}) -> Wind_type = head_side_or_tail_wind(Compass, [Headwinds, Sidewinds, Tailwinds]), {Distance, Wind_type} end, Distances_and_compass_list), [Headwind_distances, Sidewind_distances, Tailwind_distances] = lists:map( fun(Wind_type_filter) -> lists:filter( fun({_Distance, Wind_type}) -> Wind_type == Wind_type_filter end, Distances_and_wind_type_list) end, [headwind, sidewind, tailwind]), [Sum_of_headwind_distances, Sum_of_sidewind_distances, Sum_of_tailwind_distances] = lists:map( fun(Wind_type) -> lists:foldl( fun({Distance, _Wind}, Sum) -> Distance + Sum end, 0, Wind_type) end, [Headwind_distances, Sidewind_distances, Tailwind_distances]). %io:format("Direction: ~s~nSpeed: ~s mph~nGust: ~s mph~nWeather type: ~s~nTemperature: ~s deg C~n", [Direction, Speed, Gust, Weather_type, Temperature]). %Need to read API key from file readapikey() -> {_Status, Key} = file:read_file(os:getenv("HOME") ++ "/.datapoint"), string:strip(erlang:binary_to_list(Key),right,$\n). |
Changes to osrm.erl.
︙ | ︙ | |||
26 27 28 29 30 31 32 | URL = ?BASE_URL ++ ?VIAROUTE ++ "?loc=" ++ Start_lat ++ "," ++ Start_lon ++ "&loc=" ++ Finish_lat ++ "," ++ Finish_lon, %Need UA to work with OSRM API UA = "Mozilla/5.0 (X11; NetBSD i386; rv:28.0) Gecko/20100101 Firefox/28.0", try %Handling timeouts: http://stackoverflow.com/a/14143762/208793 { ok, {_Status, _Headers, Body }} = httpc:request(get, {URL, [{"User-Agent", UA}]}, [{timeout, timer:seconds(10)}], []), %For development purposes, write this out and keep it | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | URL = ?BASE_URL ++ ?VIAROUTE ++ "?loc=" ++ Start_lat ++ "," ++ Start_lon ++ "&loc=" ++ Finish_lat ++ "," ++ Finish_lon, %Need UA to work with OSRM API UA = "Mozilla/5.0 (X11; NetBSD i386; rv:28.0) Gecko/20100101 Firefox/28.0", try %Handling timeouts: http://stackoverflow.com/a/14143762/208793 { ok, {_Status, _Headers, Body }} = httpc:request(get, {URL, [{"User-Agent", UA}]}, [{timeout, timer:seconds(10)}], []), %For development purposes, write this out and keep it _Write_status = file:write_file(os:getenv("HOME") ++ "/.headsilose_route", Body), %Need to catch file write errors as well Body catch _:Reason -> io:format("API Might be down~n"), Reason after |
︙ | ︙ | |||
52 53 54 55 56 57 58 | %And these don't seem to actually be returned, hence having to go down the route of polyline decoding %Route_Instructions = proplists:get_value(<<"route_instructions">>, Props), %Total_Distance = proplists:get_value(<<"total_distance">>, proplists:get_value(<<"route_summary">>, Props)), %And need to figure out how to get nested values. I.e like xpath. Just nest the queries? Nope that doesn't work %Like so: {Hint_data} = proplists:get_value(<<"hint_data">>, Props), Checksum = proplists:get_value(<<"checksum">>, Hint_data), | | | | | > | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | %And these don't seem to actually be returned, hence having to go down the route of polyline decoding %Route_Instructions = proplists:get_value(<<"route_instructions">>, Props), %Total_Distance = proplists:get_value(<<"total_distance">>, proplists:get_value(<<"route_summary">>, Props)), %And need to figure out how to get nested values. I.e like xpath. Just nest the queries? Nope that doesn't work %Like so: {Hint_data} = proplists:get_value(<<"hint_data">>, Props), Checksum = proplists:get_value(<<"checksum">>, Hint_data), Route_geometry_as_string = binary:bin_to_list(Route_geometry), {Checksum, Route_geometry_as_string}. maybe_quit() -> Args = init:get_arguments(), Found = lists:keyfind(noshell, 1, Args), if Found =:= false -> dont_quit; true -> init:stop() end. |
Changes to polyline.erl.
1 2 3 4 5 6 7 8 9 10 | -module(polyline). -export([decode/1, eight_bit_chunks/1, six_bit_chunks/1, six_bit_chunk/1, split_up_six_bits/1, five_bit_chunks/1, bin_flip/1]). %See https://developers.google.com/maps/documentation/utilities/polylinealgorithm decode(Encoded_polyline) -> %Steps 11 back to 8 Six_bit_chunks = six_bit_chunks(Encoded_polyline), %Step 8 | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | -module(polyline). -export([decode/1, eight_bit_chunks/1, six_bit_chunks/1, six_bit_chunk/1, split_up_six_bits/1, five_bit_chunks/1, bin_flip/1]). %See https://developers.google.com/maps/documentation/utilities/polylinealgorithm decode(Encoded_polyline) -> %Steps 11 back to 8 Six_bit_chunks = six_bit_chunks(Encoded_polyline), %Step 8 Groups_of_chunks_list = split_up_six_bits(Six_bit_chunks), %Step 8 back to 6 Five_bit_chunks = five_bit_chunks(Groups_of_chunks_list), %---TODO %Maybe some more of the below need splitting out into different functions or nesting in a map? %Which option to go for, a function that maps as per five_bit_chunks, or mapping functions as per below? %I don't think I can map all functions in one go because ultimately need to change number of members in groups. %I.e. following will go from groups of five to eight. %--- %Step 5 |
︙ | ︙ | |||
49 50 51 52 53 54 55 | true -> Shifted_binary end, %Step 2 back to 1 Decoded = if Last_bit =:= "1" -> -1 * Final_binary/100000; true -> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | true -> Shifted_binary end, %Step 2 back to 1 Decoded = if Last_bit =:= "1" -> -1 * Final_binary/100000; true -> Final_binary/100000 end, Decoded end, Eight_bit_chunks), Results. %Step 8 - Split up six bit chunks, per the 0x20 bit split_up_six_bits(Bit_chunks_list) -> split_up_six_bits_(Bit_chunks_list, [], []). split_up_six_bits_([Head | Tail], Group_of_bit_chunks, Groups_of_bit_chunks_list) when [hd(Head)] == "1" -> split_up_six_bits_(Tail, [Head]++Group_of_bit_chunks, Groups_of_bit_chunks_list); split_up_six_bits_([Head | Tail], Group_of_bit_chunks, Groups_of_bit_chunks_list) when [hd(Head)] == "0" -> %Then need to start a new list, but after this 0 one! split_up_six_bits_(Tail, [], [lists:reverse([Head]++Group_of_bit_chunks)]++Groups_of_bit_chunks_list); split_up_six_bits_([], Group_of_bit_chunks, Groups_of_bit_chunks_list) when length(Group_of_bit_chunks) > 0 -> split_up_six_bits_([], [], [lists:reverse(Group_of_bit_chunks)]++Groups_of_bit_chunks_list); split_up_six_bits_([], [], Groups_of_bit_chunks_list) -> %TODO Might be neater to map lists:reverse over the list instead of doing above and here. lists:reverse(Groups_of_bit_chunks_list). %Step 5 %TODO See if better way of doing this eight_bit_chunks(Five_bit_chunks_list) -> Five_bit_chunk_string = lists:reverse(lists:flatten(Five_bit_chunks_list)), eight_bit_chunks_(Five_bit_chunk_string, []). eight_bit_chunks_(Five_bit_chunk_string, Eight_bit_chunks_list) when length(Five_bit_chunk_string) > 8 -> Eight_bit_chunk = lists:reverse(lists:sublist(Five_bit_chunk_string,1,8)), Rest_of_five_bit_chunk_string = lists:nthtail(8,Five_bit_chunk_string), eight_bit_chunks_(Rest_of_five_bit_chunk_string, [Eight_bit_chunk]++Eight_bit_chunks_list); eight_bit_chunks_(Five_bit_chunk_string, Eight_bit_chunks_list) when length(Five_bit_chunk_string) =< 8, Five_bit_chunk_string /= [] -> Padded_bit_string = pad_to(8, lists:reverse(Five_bit_chunk_string)), eight_bit_chunks_([], [Padded_bit_string]++Eight_bit_chunks_list); eight_bit_chunks_([], Eight_bit_chunks_list) -> Eight_bit_chunks_list. six_bit_chunks(Encoded_polyline) -> six_bit_chunks_(Encoded_polyline, []). six_bit_chunks_([Head | Rest], Chunks_list) -> Six_bit_chunk = six_bit_chunk(Head), %Add to Reversed_chunks six_bit_chunks_(Rest, [Six_bit_chunk]++Chunks_list); six_bit_chunks_([], Chunks_list) -> lists:reverse(Chunks_list). six_bit_chunk(Ascii_bit) -> %Step 10 Shifted_bit = Ascii_bit - 63, %Step 9 %From http://erlangcentral.org/wiki/index.php/Converting_Between_Binary_and_Decimal Binary_chunk = hd(io_lib:format("~.2B", [Shifted_bit])), %---TODO %What if Binary_chunk is shorter than 6? %Well in that case, I guess that means we'd want to split, but for now pad to six and check for 0x20 elsewhere. %--- pad_to(6, Binary_chunk). five_bit_chunks(Groups_of_chunks_list) -> lists:map( fun(Group_of_chunks) -> %Step 7 - Un-reverse the five bit chunks lists:reverse(lists:map( fun(Chunk) -> %Step 8 - "Un-or" the 0x20 bit lists:sublist(Chunk,2,6) end, Group_of_chunks)) end, Groups_of_chunks_list). %I can't figure out padding with io:format etc when printing binary numbers pad_to(Length, Binary_string) when length(Binary_string) < Length -> Padded_binary_string = "0"++Binary_string, pad_to(Length, Padded_binary_string); pad_to(Length, Binary_string) when length(Binary_string) == Length -> Binary_string. %bnot doesn't seem to work as I thought it would so do it very inelegantly by switching each "bit" in a string. bin_flip(Binary_number) -> Binary_string = hd(io_lib:format("~.2B", [Binary_number])), bin_flip_(Binary_string, []). bin_flip_([Head | Rest], Flipped_string) -> Head_bit = hd(io_lib:format("~c",[Head])), Flipped_bit = if Head_bit =:= "0" -> "1"; true -> "0" |
︙ | ︙ |