Heads-I-Lose

Check-in [301914a6a1]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Housekeeping: Formatting, variable naming consistency, etc

- Get rid of the mixed up mess of CamelCase, underscores, prefixes,
suffixes, abbreviations, full names, etc.
- Make indenting of lists:map functions consistent
- Remove spurious whitespace

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | origin/master
Files: files | file ages | folders
SHA3-256: 301914a6a181661f7bdf5bb0ad9e430a1e2742ac50f07f9a7217f49158096477
User & Date: base@atomicules.co.uk 2014-12-05 20:43:53
Context
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
wanted to do. Sure they will be much room for improvements to the code,
but it works! check-in: c4d0b2bbec user: base@atomicules.co.uk tags: origin/master, trunk

20:43
Housekeeping: Formatting, variable naming consistency, etc

- Get rid of the mixed up mess of CamelCase, underscores, prefixes,
suffixes, abbreviations, full names, etc.
- Make indenting of lists:map functions consistent
- Remove spurious whitespace check-in: 301914a6a1 user: base@atomicules.co.uk tags: origin/master, trunk

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
naming of variables - it's such a mess. check-in: 6912ce6208 user: base@atomicules.co.uk tags: origin/master, trunk

Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to headsilose.erl.

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").











|







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
76

77
78
79
80
81
82
83
	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"),







|
>







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
108
109
110
111
112
113
114
115
116
	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
	DateAsString = io_lib:format("~4..0w-~2..0w-~2..0wZ", [Year, Month, Day]),
	lists:flatten(DateAsString).


nth_wrap(N, List) ->
	Rem = N rem (length(List)),
	if Rem > 0 ->
		lists:nth(Rem, List);
	Rem =:= 0 ->







|
|







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
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


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
	HeadwindList = lists:seq(14,18),
	SidewindList = lists:seq(3,5)++lists:seq(11,13),
	TailwindList = lists:seq(6,10),
	[Headwinds, Sidewinds, Tailwinds] = lists:map(fun(WindList) ->

		lists:map(fun(X) ->

			nth_wrap(Index+X, Compass) end,

			WindList)
		end,
		[HeadwindList, SidewindList, TailwindList]).


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]], List_distance_headings) ->
	%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}]++List_distance_headings);
convert_lats_longs_to_distance_heading_([], List_distance_headings) ->
	lists:reverse(List_distance_headings).


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),
	List_of_distances_and_compass = convert_lats_longs_to_distance_heading(Polyline_decoded),
	%If now have a set of co-ords need to figure out distances and directions

	List_of_distances_and_wind = lists:map(fun({Distance, Compass}) ->
			Wind = head_side_or_tail_wind(Compass, [Headwinds, Sidewinds, Tailwinds]),
			{Distance, Wind}
		end,
		List_of_distances_and_compass),

	[Headw, Sidew, Tailw] = lists:map(fun(Wind_group) ->

		lists:filter(fun({_Distance, Wind_type}) ->
			Wind_type == Wind_group end,

			List_of_distances_and_wind) end,

		[headwind, sidewind, tailwind]),

	[Sum_of_Headw, Sum_of_Sidew, Sum_of_Tailw] = lists:map(fun(Windg) ->
		lists:foldl(fun({Distance2, _Wind}, Sum) -> Distance2 + Sum end, 0, Windg) end,






		[Headw, Sidew, Tailw]).
	%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, KeyB} = file:read_file(os:getenv("HOME") ++ "/.datapoint"),
	string:strip(erlang:binary_to_list(KeyB),right,$\n).







|
>
|










|
|
|
|
>
|
>
|
>
|

|





|










|
|
|











|
>
|
>



















|

>
|
|
|

|
>
|
>
|
|
>
|
>

>
|
|
>
>
>
>
>
>
|

<



|
|
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
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
		_WriteStatus = 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







|







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
59
60
61
62
63
64
65
66
67
68

69
	%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_geometryS = binary:bin_to_list(Route_geometry),
	{Checksum, Route_geometryS}.


maybe_quit() ->
	Args = init:get_arguments(), 
	Found = lists:keyfind(noshell, 1, Args),
	if Found =:= false ->
		dont_quit;
	true -> init:stop()

	end.	







|
|



|



|
>
|
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
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
	List_of_groups_of_chunks = split_up_six_bits(Six_bit_chunks),
	%Step 8 back to 6
	Five_bit_chunks = five_bit_chunks(List_of_groups_of_chunks),
	%---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










|

|







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
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(List_of_bit_chunks) ->
	split_up_six_bits_(List_of_bit_chunks, [], []).
split_up_six_bits_([Head | Tail], Group_of_bit_chunks, List_of_groups_of_bit_chunks) when [hd(Head)] == "1" ->
	split_up_six_bits_(Tail, [Head]++Group_of_bit_chunks, List_of_groups_of_bit_chunks);
split_up_six_bits_([Head | Tail], Group_of_bit_chunks, List_of_groups_of_bit_chunks) 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)]++List_of_groups_of_bit_chunks);	
split_up_six_bits_([], Group_of_bit_chunks, List_of_groups_of_bit_chunks) when length(Group_of_bit_chunks) > 0 ->
	split_up_six_bits_([], [], [lists:reverse(Group_of_bit_chunks)]++List_of_groups_of_bit_chunks);
split_up_six_bits_([], [], List_of_groups_of_bit_chunks) ->
	%TODO Might be neater to map lists:reverse over the list instead of doing above and here.
	lists:reverse(List_of_groups_of_bit_chunks).


%Step 5
%TODO See if better way of doing this
eight_bit_chunks(List_of_five_bit_chunks) ->
	Five_bit_chunk_string = lists:reverse(lists:flatten(List_of_five_bit_chunks)),
	eight_bit_chunks_(Five_bit_chunk_string, []).
eight_bit_chunks_(Five_bit_chunk_string, List_of_eight_bit_chunks) 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]++List_of_eight_bit_chunks);
eight_bit_chunks_(Five_bit_chunk_string, List_of_eight_bit_chunks) 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]++List_of_eight_bit_chunks);
eight_bit_chunks_([], List_of_eight_bit_chunks) ->
	List_of_eight_bit_chunks.


six_bit_chunks(Encoded_polyline) ->
	six_bit_chunks_(Encoded_polyline, []).
six_bit_chunks_([Head | Rest], List_of_chunks) ->
	Six_bit_chunk = six_bit_chunk(Head),
	%Add to Reversed_chunks
	six_bit_chunks_(Rest, [Six_bit_chunk]++List_of_chunks);
six_bit_chunks_([], List_of_chunks) ->
	lists:reverse(List_of_chunks).


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(List_of_groups_of_chunks) ->
	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,
		List_of_groups_of_chunks).


%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"







|








|
|
|
|
|

|
|
|
|

|




|
|

|


|
|

|
|
|




|


|
|
|















|










|








|



|







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"