Skip to content

Commit

Permalink
Merge pull request #48 from jkuznik/Task-40-Google-Maps
Browse files Browse the repository at this point in the history
Task-40-Google-Maps
  • Loading branch information
jkuznik authored Nov 26, 2024
2 parents 13f891b + 3c31ee8 commit e320ea1
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 30 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/self-host-CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ jobs:
TWILIO_AUTH_TOKEN: ${{secrets.TWILIO_AUTH_TOKEN}}
TWILIO_PHONE_NUMBER: ${{secrets.TWILIO_PHONE_NUMBER}}
OPENAI_API_KEY: ${{secrets.OPENAI_API_KEY}}
GOOGLE_API_KEY: ${{secrets.GOOGLE_API_KEY}}
DAY_SMS_LIMIT: 5
run: sudo docker run -d -p 8080:8080 --name disaster-alert-container --network disaster-net -e TWILIO_ACCOUNT_SID=$TWILIO_ACCOUNT_SID -e TWILIO_AUTH_TOKEN=$TWILIO_AUTH_TOKEN -e TWILIO_PHONE_NUMBER=$TWILIO_PHONE_NUMBER -e OPENAI_API_KEY=$OPENAI_API_KEY -e DAY_SMS_LIMIT=$DAY_SMS_LIMIT jkuznik/disaster-alert
run: sudo docker run -d -p 8080:8080 --name disaster-alert-container --network disaster-net -e TWILIO_ACCOUNT_SID=$TWILIO_ACCOUNT_SID -e TWILIO_AUTH_TOKEN=$TWILIO_AUTH_TOKEN -e TWILIO_PHONE_NUMBER=$TWILIO_PHONE_NUMBER -e OPENAI_API_KEY=$OPENAI_API_KEY -e GOOGLE_API_KEY=$GOOGLE_API_KEY -e DAY_SMS_LIMIT=$DAY_SMS_LIMIT jkuznik/disaster-alert
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import pl.ateam.disasteralerts.disasteralert.dto.DisasterDTO;

import java.util.List;
import java.util.Map;
import java.util.Optional;

@Component
Expand All @@ -25,4 +26,8 @@ public DisasterDTO createDisaster(@NotNull @Valid DisasterAddDTO disasterAddDTO,
public List<DisasterDTO> interestingDisasters(Optional<DisasterType> type, Optional<String> location) {
return disasterService.interestingDisasters(type, location);
}

public Map<String, Integer> inLocationDisastersAmount() {
return disasterService.inLocationDisastersAmount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pl.ateam.disasteralerts.disasteralert.dto.DisasterDTO;

import java.util.List;
import java.util.Map;
import java.util.Optional;

@Validated
Expand All @@ -17,4 +18,6 @@ interface DisasterService {

List<DisasterDTO> interestingDisasters(Optional<DisasterType> type, Optional<String> location);

Map<String, Integer> inLocationDisastersAmount();

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
import pl.ateam.disasteralerts.disasteralert.dto.DisasterAddDTO;
import pl.ateam.disasteralerts.disasteralert.dto.DisasterDTO;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;

@Service(value = "prototype")
Expand Down Expand Up @@ -91,4 +88,15 @@ public List<DisasterDTO> interestingDisasters(Optional<DisasterType> type, Optio

return interestedDisasters;
}

@Override
public Map<String, Integer> inLocationDisastersAmount() {
List<Disaster> allActiveDisasters = disasterRepository.findAllByStatus(DisasterStatus.ACTIVE);

return allActiveDisasters.stream()
.collect(Collectors.groupingBy(
Disaster::getLocation,
Collectors.summingInt(e -> 1)
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import pl.ateam.disasteralerts.disasteralert.DisasterAlertFacade;
import pl.ateam.disasteralerts.disasteralert.DisasterStatus;
Expand All @@ -20,16 +17,18 @@
import pl.ateam.disasteralerts.security.AppUser;
import pl.ateam.disasteralerts.util.CitiesInPoland;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Controller
@RequiredArgsConstructor
@RequestMapping("/disasters")
public class DisasterViewController {

@Value("${google.maps.api.key}")
private String googleApiKey;

private final DisasterAlertFacade disasterAlertFacade;
private final String USER_AS_DISASTER_SOURCE = "user";

Expand All @@ -41,6 +40,7 @@ public String showAddDisasterForm(Model model, @AuthenticationPrincipal AppUser
model.addAttribute("disasterTypSelected", null);
model.addAttribute("disasterAddDTO", new DisasterAddDTO(null, null, null, null));
model.addAttribute("selectedLocation", appUser.getUserDTO().location());
model.addAttribute("googleApiKey", googleApiKey);

return "addDisaster";
}
Expand All @@ -66,10 +66,11 @@ public String createDisaster(Model model, @AuthenticationPrincipal AppUser userD
@GetMapping("list")
public String showDisasterList(Model model, @AuthenticationPrincipal AppUser userDetails) {
baseModel(model, userDetails);

if (!model.containsAttribute("list")) {

model.addAttribute("list", getDisasterDTOS("Wszystkie", "Wszystkie"));
}
model.addAttribute("googleApiKey", googleApiKey);
model.addAttribute("inLocationDisasterAmount", inLocationDisastersAmount());

return "listDisasters";
}

Expand All @@ -82,16 +83,20 @@ public String filterList(@RequestParam(name = "disasterType") String disasterTyp
return "redirect:/disasters/list";
}

private Map<String, Integer> inLocationDisastersAmount() {
return disasterAlertFacade.inLocationDisastersAmount();
}

private List<DisasterDTO> getDisasterDTOS(String disasterType, String city) {
Optional<DisasterType> type;
if (disasterType.isEmpty() || disasterType.equals("Wszystkie")){
if (disasterType.isEmpty() || disasterType.equals("Wszystkie")) {
type = Optional.empty();
} else {
type = Optional.of(DisasterType.valueOf(disasterType));
}

Optional<String> location;
if (city.isEmpty() || city.equals("Wszystkie")){
if (city.isEmpty() || city.equals("Wszystkie")) {
location = Optional.empty();
} else {
location = Optional.of(city);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
Expand All @@ -21,12 +22,16 @@
@RequiredArgsConstructor
public class UserUpdateViewController {

@Value("${google.maps.api.key}")
private String googleApiKey;

private final UserFacade userFacade;

@GetMapping("edit")
public String getUserForUpdate(@AuthenticationPrincipal AppUser appUser, Model model) {
UserUpdateDTO userUpdateDto = userFacade.getUserForUpdate(appUser.getUserDTO().id());
model.addAttribute("userUpdateDto", userUpdateDto);
model.addAttribute("googleApiKey", googleApiKey);
baseModel(model, userUpdateDto);
return "updateUser";
}
Expand Down
6 changes: 5 additions & 1 deletion src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ server:
openweathermap:
api:
key: 3261c3b28f5aed55fcca580f4d7744d0
url: https://api.openweathermap.org/data/2.5/weather
url: https://api.openweathermap.org/data/2.5/weather

google.maps:
api:
key: ${GOOGLE_API_KEY}
76 changes: 64 additions & 12 deletions src/main/resources/templates/fragments.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@
<div class="text-danger" th:if="${#fields.errors('location')}" th:errors="*{location}"></div>
<h2>Wybierz miejscowość:</h2>
<div class="form-group">
<input th:value="${selectedLocation}" type="text" class="form-control" id="cityInput"
<input th:value="${selectedLocation}" type="text" class="form-control my-2" id="cityInput"
placeholder="Zacznij wpisywać" onkeyup="filterCities()">

<select class="form-select" id="location" name="location" size="5">
<select class="form-select" id="location" name="location" size="5" onchange="updateMap(); updateLocationFieldFromSelect()">
<option th:each="location : ${cities}"
th:value="${location}"
th:text="${location}"
Expand All @@ -97,24 +97,76 @@ <h2>Wybierz miejscowość:</h2>
</select>
</div>

<div id="map" style="width: 100%; height: 450px;" class="mt-4"></div>

<script>
<script th:src="'https://maps.googleapis.com/maps/api/js?key=' + ${googleApiKey} + '&callback=initMap'" async defer></script>

<script>
function filterCities() {
let input = document.getElementById('cityInput').value.toLowerCase();
let dropdown = document.getElementById('location');
let options = dropdown.getElementsByTagName('option');

for (let i = 0; i < options.length; i++) {
let txtValue = options[i].textContent || options[i].innerText;
if (txtValue.toLowerCase().indexOf(input) > -1) {
options[i].style.display = "";
} else {
options[i].style.display = "none";
for (let i = 0; i < options.length; i++) {
let txtValue = options[i].textContent || options[i].innerText;
if (txtValue.toLowerCase().indexOf(input) > -1) {
options[i].style.display = "";
} else {
options[i].style.display = "none";
}
}
}
}
</script>
</div>

function updateMap() {
let selectedLocation = document.getElementById('location').value;
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': selectedLocation }, function(results, status) {
if (status == 'OK') {
map.setCenter(results[0].geometry.location);
marker.setPosition(results[0].geometry.location);
}
});
}

function updateLocationFieldFromSelect() {
let selectedLocation = document.getElementById('location').value;
document.getElementById('cityInput').value = selectedLocation;
}

let map;
let marker;

function initMap() {
const defaultLocation = {lat: 52.2297, lng: 21.0122};

map = new google.maps.Map(document.getElementById("map"), {
center: defaultLocation,
zoom: 6,
});

marker = new google.maps.Marker({
position: defaultLocation,
map: map,
draggable: true
});

map.addListener("click", (event) => {
placeMarker(event.latLng);
updateLocationField(event.latLng);
});
}

function placeMarker(location) {
marker.setPosition(location);
map.panTo(location);
}

function updateLocationField(location) {
const locationInput = document.getElementById("cityInput");
locationInput.value = `${location.lat()}, ${location.lng()}`;
}
</script>
</div>

</body>
</html>
70 changes: 70 additions & 0 deletions src/main/resources/templates/listDisasters.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<style>
#map {
width: 50%;
height: 35%;
margin: 0 auto;
margin-left: 25%;
margin-right: 25%;
}
html, body {
height: 100%;
margin: 0;
padding: 10;
}
</style>

<head th:replace="~{fragments :: head('Lista zdarzeń')}"></head>

<body class="bg-body-tertiary">
Expand All @@ -11,6 +26,61 @@ <h1 class="display-5 fw-bold text-body-emphasis">Sprawdź <span class="text-prim
<p class="lead">Sprawdź informacje o aktywnych zdarzeniach.</p>
</div>
</div>

<div id="map"></div>

<script th:inline="javascript">
// Thymeleaf wstawia dane do skryptu w formacie JSON
const cityCounts = /*[[${inLocationDisasterAmount}]]*/ {};

// Sprawdzamy, czy cityCounts zawiera dane
console.log(cityCounts);

// Funkcja do uzyskiwania współrzędnych geograficznych dla miasta
function getLatLng(city, callback) {
const geocoder = new google.maps.Geocoder();

geocoder.geocode({ address: city }, function(results, status) {
if (status === 'OK') {
const location = results[0].geometry.location;
callback(location.lat(), location.lng());
} else {
console.error("Geocode error: " + status);
}
});
}

function initMap() {
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 52.0, lng: 19.0 },
zoom: 6
});

if (Object.keys(cityCounts).length > 0) {
for (const [city, count] of Object.entries(cityCounts)) {
getLatLng(city, (lat, lng) => {
const marker = new google.maps.Marker({
position: { lat, lng },
map: map,
label: count.toString()
});

const infoWindow = new google.maps.InfoWindow({
content: `Miasto: ${city}<br>Liczba zdarzeń: ${count}`
});

marker.addListener('click', () => {
infoWindow.open(map, marker);
});
});
}
} else {
console.log("Brak danych o miastach.");
}
}
</script>

<script th:src="'https://maps.googleapis.com/maps/api/js?key=' + ${googleApiKey} + '&callback=initMap'" async defer></script>
<div>
<div class="container col-xl-10 col-xxl-10 px-4 ">
<div class="row g-lg-5 py-5">
Expand Down

0 comments on commit e320ea1

Please sign in to comment.