Packages

A few sentences (a paragraph) describing the project.

Current section

Files

Jump to
cwikla_pollution lib pollution.erl
Raw

lib/pollution.erl

-module(pollution).
-author("Arkadiusz Cwikla").
%% API
-export([createMonitor/0,
addStation/3, addValue/5, removeValue/4,
getOneValue/4, getStationMean/3, getDailyMean/3,
getDailyMaxMean/3, getDailyMinMean/3, getMinimumDistanceStations/3]).
%% records
-record(measurement, {type, value, date}).
-record(monitor, {stations, measurements}).
%%createMonitor/0 - tworzy i zwraca nowy monitor zanieczyszczeń;
%%addStation/3 - dodaje do monitora wpis o nowej stacji pomiarowej (nazwa i współrzędne geograficzne), zwraca zaktualizowany monitor;
%%addValue/5 - dodaje odczyt ze stacji (współrzędne geograficzne lub nazwa stacji, data, typ pomiaru, wartość), zwraca zaktualizowany monitor;
%%removeValue/4 - usuwa odczyt ze stacji (współrzędne geograficzne lub nazwa stacji, data, typ pomiaru), zwraca zaktualizowany monitor;
%%getOneValue/4 - zwraca wartość pomiaru o zadanym typie, z zadanej daty i stacji;
%%getStationMean/3 - zwraca średnią wartość parametru danego typu z zadanej stacji;
%%getDailyMean/3 - zwraca średnią wartość parametru danego typu, danego dnia na wszystkich stacjach;
%% monitor to krotka slownikow coordinates - name oraz name - lista measurement
createMonitor() -> #monitor{stations = dict:new(), measurements = dict:new()}.
%% funkcje pomocnicze do sprawdzania warunkow
coordinatesUsed(Coordinates, #monitor{stations = Stations}) -> dict:is_key(Coordinates, Stations).
nameUsed(StationName, #monitor{measurements = Measurements}) -> dict:is_key(StationName, Measurements).
%% sprawdzanie spojnosci danych i ewentualne dodawanie stacji
addStation(StationName, Coordinates, Monitor) ->
case {coordinatesUsed(Coordinates, Monitor), nameUsed(StationName, Monitor)} of
{true, true} ->
%% io:format("~p ~n", ["Coordinates and station name are already used"]),
Monitor;
{true, false} ->
%% io:format("~p ~n", ["Coordinates are already used"]),
Monitor;
{false, true} ->
%% io:format("~p ~n", ["Station name is already used"]),
Monitor;
{false, false} -> addNewStation(StationName, Coordinates, Monitor)
end
.
%% dodawanie stacji
addNewStation(StationName, Coordinates, #monitor{stations = Stations, measurements = Measurements}) ->
NewStations = dict:store(Coordinates, StationName, Stations),
NewMeasurements = dict:append_list(StationName, [], Measurements),
%% io:format("~p ~n", ["Added new station"]),
#monitor{stations = NewStations, measurements = NewMeasurements}
.
%% obsluga dodawania wartosci w zaleznosci od tego, czy podano nazwe czy wspolrzedne
addValue(StationName, Date, Type, Value, Monitor) when is_list(StationName) ->
case nameUsed(StationName, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect station name"]),
Monitor;
true -> addNewValueUsingName(StationName, Date, Type, Value, Monitor)
end;
addValue(StationCoords, Date, Type, Value, Monitor) when is_tuple(StationCoords) ->
case coordinatesUsed(StationCoords, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect coordinates"]),
Monitor;
true -> addNewValueUsingCoords(StationCoords, Date, Type, Value, Monitor)
end
.
%% funkcja pomocnicza
getStationName(StationCoords, #monitor{stations = Stations}) ->
dict:fetch(StationCoords, Stations)
.
%% owijka dla przypadku uzycia wspolrzednych
addNewValueUsingCoords(StationCoords, Date, Type, Value, Monitor) ->
StationName = getStationName(StationCoords, Monitor),
addNewValueUsingName(StationName, Date, Type, Value, Monitor)
.
%% dodawanie wartosci
addNewValueUsingName(StationName, Date, Type, Value, Monitor) ->
#monitor{measurements = Measurements} = Monitor,
StoredMeasurements = dict:fetch(StationName, Measurements),
case checkMeasurement(Date, Type, StoredMeasurements) of
{_, true} ->
%% io:format("~p ~n", ["Added new measurement"]),
Measurement = #measurement{type = Type, value = Value, date = Date},
NewMeasurements = dict:append(StationName, Measurement, Measurements),
Monitor#monitor{measurements = NewMeasurements};
{_, false} ->
%% io:format("~p ~n", ["You cannot add the same measurement twice"]),
Monitor
end
.
%% wazna funkcja pomocnicza, sprawdza czy pomiar o danej dacie i typie juz istnieje w danej stacji
%% zwraca pusta tablice i true, gdy nie istnieje
%% zwraca tablice z istniejacym pomiarem i false, gdy istnieje
checkMeasurement(Date, Type, StoredMeasurements) ->
EqualDateAndType =
fun(#measurement{date = StoredDate, type = StoredType}) ->
(StoredDate == Date) and (StoredType == Type)
end,
FilteredMeasurements = lists:filter(EqualDateAndType, StoredMeasurements),
case FilteredMeasurements of
[] -> {[], true};
[Measurement] -> {[Measurement], false}
end
.
%% obsluga usuwania wartosci, w zaleznosci od tego, czy podano nazwe czy wspolrzedne
removeValue(StationName, Date, Type, Monitor) when is_list(StationName) ->
case nameUsed(StationName, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect station name"]),
Monitor;
true -> removeValueUsingName(StationName, Date, Type, Monitor)
end;
removeValue(StationCoords, Date, Type, Monitor) when is_tuple(StationCoords) ->
case coordinatesUsed(StationCoords, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect coordinates"]),
Monitor;
true -> removeValueUsingCoords(StationCoords, Date, Type, Monitor)
end
.
%% owijka dla przypadku uzycia wspolrzednych
removeValueUsingCoords(StationCoords, Date, Type, Monitor) ->
StationName = getStationName(StationCoords, Monitor),
removeValueUsingName(StationName, Date, Type, Monitor)
.
%% usuwanie pomiaru z danej stacji
removeValueUsingName(StationName, Date, Type, Monitor) ->
#monitor{measurements = Measurements} = Monitor,
StoredMeasurements = dict:fetch(StationName, Measurements),
{Measurement, _} = checkMeasurement(Date, Type, StoredMeasurements),
NewMeasurements = dict:store(StationName, StoredMeasurements -- Measurement, Measurements),
%% io:format("~p ~n", ["Removed measurement"]),
Monitor#monitor{measurements = NewMeasurements}
.
%% obsluga czytania wartosci, w zaleznosci od tego, czy podano nazwe czy wspolrzedne
getOneValue(StationName, Date, Type, Monitor) when is_list(StationName) ->
case nameUsed(StationName, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect station name"]),
Monitor;
true -> getValueUsingName(StationName, Date, Type, Monitor)
end;
getOneValue(StationCoords, Date, Type, Monitor) when is_tuple(StationCoords) ->
case coordinatesUsed(StationCoords, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect coordinates"]),
Monitor;
true -> getValueUsingCoords(StationCoords, Date, Type, Monitor)
end
.
%% owijka dla przypadku uzycia wspolrzednych
getValueUsingCoords(StationCoords, Date, Type, Monitor) ->
StationName = getStationName(StationCoords, Monitor),
getValueUsingName(StationName, Date, Type, Monitor)
.
%% czytanie wartosci
getValueUsingName(StationName, Date, Type, Monitor) ->
#monitor{measurements = Measurements} = Monitor,
StoredMeasurements = dict:fetch(StationName, Measurements),
case checkMeasurement(Date, Type, StoredMeasurements) of
{[], true} ->
%% io:format("~p ~n", ["No such value"]),
no_value;
{[Measurement], false} ->
#measurement{value = Value} = Measurement,
Value
end
.
%% obliczanie sredniej dla stacji, w zaleznosci od tego, czy podano nazwe czy wspolrzedne
getStationMean(StationName, Type, Monitor) when is_list(StationName) ->
case nameUsed(StationName, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect station name"]),
Monitor;
true -> getStationMeanUsingName(StationName, Type, Monitor)
end;
getStationMean(StationCoords, Type, Monitor) when is_tuple(StationCoords) ->
case coordinatesUsed(StationCoords, Monitor) of
false ->
%% io:format("~p ~n", ["Incorrect coordinates"]),
Monitor;
true -> getStationMeanUsingCoords(StationCoords, Type, Monitor)
end
.
%% owijka dla przypadku uzycia wspolrzednych
getStationMeanUsingCoords(StationCoords, Type, Monitor) ->
StationName = getStationName(StationCoords, Monitor),
getStationMeanUsingName(StationName, Type, Monitor)
.
%% obliczenie sredniej dla stacji
getStationMeanUsingName(StationName, Type, Monitor) ->
#monitor{measurements = Measurements} = Monitor,
StoredMeasurements = dict:fetch(StationName, Measurements),
MatchingTypes =
fun(#measurement{type = StoredType}) ->
(StoredType == Type)
end,
FilteredMeasurements = lists:filter(MatchingTypes, StoredMeasurements),
getMean(FilteredMeasurements)
.
%% obliczanie sredniej dziennej dla typu
getDailyMean(Type, {Date, _Hour}, Monitor) ->
#monitor{measurements = Measurements} = Monitor,
Concat =
fun(_, Measurement, Acc) ->
Acc ++ Measurement
end,
ConcatenatedMeasurements = dict:fold(Concat, [], Measurements),
MatchingTypesAndDates =
fun(#measurement{type = StoredType, date = {StoredDate, _StoredHour}}) ->
(StoredType == Type) and (StoredDate == Date)
end,
FilteredMeasurements = lists:filter(MatchingTypesAndDates, ConcatenatedMeasurements),
getMean(FilteredMeasurements)
.
%% pomocnicza funkcja do obliczania srednich
getMean(Measurements) ->
SumValuesAndOccurrences =
fun(#measurement{value = Value}, {Sum, Occurrences}) ->
{Sum + Value, Occurrences + 1}
end,
{Sum, Occurrences} = lists:foldl(SumValuesAndOccurrences, {0, 0}, Measurements),
%% io:format("~w ~w ~n", [Sum, Occurrences]),
Sum / Occurrences
.
%% dodatkowe funckje
%% getDailyMaxMean/3 - wyszukuje maksymalna srednia wartosc parametru dla danego dnia i zwraca krotke {nazwa, wartosc}
%% getDailyMinMean/3 - wyszukuje minimalna srednia wartosc parametru dla danego dnia i zwraca krotke {nazwa, wartosc}
getDailyMaxMean(Type, {Date, _Hour}, Monitor) ->
Means = getDailyMeans(Type, Date, Monitor),
MaxMean =
fun(Name, Value, {_MaxName, MaxValue}) when (Value > MaxValue) -> {Name, Value};
(_Name, Value, {MaxName, MaxValue}) when not(Value > MaxValue) -> {MaxName, MaxValue}
end,
dict:fold(MaxMean, {"", -999999999}, Means)
.
getDailyMinMean(Type, {Date, _Hour}, Monitor) ->
Means = getDailyMeans(Type, Date, Monitor),
MinMean =
fun(Name, Value, {_MinName, MinValue}) when (Value < MinValue) -> {Name, Value};
(_Name, Value, {MinName, MinValue}) when not(Value < MinValue) -> {MinName, MinValue}
end,
dict:fold(MinMean, {"", 999999999}, Means)
.
%% pomocnicza funkcja
getDailyMeans(Type, Date, Monitor) ->
#monitor{measurements = StoredMeasurements} = Monitor,
%% io:format("~s ~n ~w ~n", ["StoredMeasurements", StoredMeasurements]),
MatchingTypesAndDates =
fun(#measurement{type = StoredType, date = {StoredDate, _StoredHour}}) ->
(StoredType == Type) and (StoredDate == Date)
end,
MapFilteredLists =
fun(_, Measurements) ->
length(lists:filter(MatchingTypesAndDates, Measurements)) > 0
end,
FilteredMeasurements = dict:filter(MapFilteredLists, StoredMeasurements),
MapMeans =
fun(_, Measurements) ->
getMean(Measurements)
end,
%% io:format("~s ~n ~w ~n", ["FilteredMeasurements", FilteredMeasurements]),
dict:map(MapMeans, FilteredMeasurements)
.
% trzecia dodatkowa funckja z excela
% zwraca liste koordynatów stacji które są w odpowiedniej odleglosci od danego punktu
getMinimumDistanceStations(Coords, Distance, #monitor{stations = Stations}) ->
CoordsList = dict:to_list(Stations),
InDistance =
fun ({CurrentCoords, _Name}) -> distance(CurrentCoords, Coords) < Distance end,
Filtered = lists:filter(InDistance, CoordsList),
lists:map(fun ({CurrentCoords, _Name})-> CurrentCoords end, Filtered)
.
%% haversin zakoszony z forum
distance({Lng1, Lat1}, {Lng2, Lat2}) ->
Deg2rad = fun(Deg) -> math:pi()*Deg/180 end,
[RLng1, RLat1, RLng2, RLat2] = [Deg2rad(Deg) || Deg <- [Lng1, Lat1, Lng2, Lat2]],
DLon = RLng2 - RLng1,
DLat = RLat2 - RLat1,
A = math:pow(math:sin(DLat/2), 2) + math:cos(RLat1) * math:cos(RLat2) * math:pow(math:sin(DLon/2), 2),
C = 2 * math:asin(math:sqrt(A)),
%% suppose radius of Earth is 6372.8 km
Km = 6372.8 * C,
Km.