├── routing_frontend ├── img │ ├── branch.png │ ├── marker.png │ └── marker-green.png ├── .idea │ ├── .gitignore │ ├── misc.xml │ ├── modules.xml │ └── routing_frontend.iml ├── index.html ├── pages │ ├── home │ │ └── home.html │ ├── dispatch │ │ ├── dispatch.html │ │ └── dispatch.js │ ├── dispatch-details │ │ ├── dispatch-details.html │ │ └── dispatch-details.js │ ├── routing │ │ ├── routing.html │ │ └── routing.js │ ├── dispatch-distribution │ │ ├── dispatch-distribution.html │ │ └── dispatch-distribution.js │ └── add-dispatch │ │ ├── add-dispatch.html │ │ └── add-dispatch.js ├── index.js └── helper.js ├── routing_backend ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── routing_backend │ │ │ │ ├── enums │ │ │ │ ├── DeliveryRange.java │ │ │ │ ├── VehicleType.java │ │ │ │ └── DispatchType.java │ │ │ │ ├── dto │ │ │ │ ├── ORSSummary.java │ │ │ │ ├── ORSEngine.java │ │ │ │ ├── ORSSegment.java │ │ │ │ ├── ORSQuery.java │ │ │ │ ├── ORSMetadata.java │ │ │ │ ├── ORSDirectionRequest.java │ │ │ │ ├── ORSRoute.java │ │ │ │ ├── ORSDirectionResponse.java │ │ │ │ └── ORSStep.java │ │ │ │ ├── repository │ │ │ │ ├── VehicleRepository.java │ │ │ │ ├── CustomerRepository.java │ │ │ │ ├── DispatchRepository.java │ │ │ │ ├── BranchRepository.java │ │ │ │ └── DispatchVehicleRepository.java │ │ │ │ ├── opt │ │ │ │ └── distancematrix │ │ │ │ │ ├── DistanceMatrix.java │ │ │ │ │ ├── OptCoordinates.java │ │ │ │ │ ├── Distance.java │ │ │ │ │ ├── provider │ │ │ │ │ ├── DistanceProvider.java │ │ │ │ │ └── GraphHopperDistanceProvider.java │ │ │ │ │ └── DistanceMatrixService.java │ │ │ │ ├── entity │ │ │ │ ├── Customer.java │ │ │ │ ├── Branch.java │ │ │ │ ├── DispatchVehicle.java │ │ │ │ ├── Vehicle.java │ │ │ │ └── Dispatch.java │ │ │ │ ├── RoutingBackendApplication.java │ │ │ │ ├── utility │ │ │ │ ├── BoundingBoxUtility.java │ │ │ │ ├── DeliveryRangeUtility.java │ │ │ │ └── DateUtility.java │ │ │ │ ├── service │ │ │ │ ├── BranchService.java │ │ │ │ ├── CustomerService.java │ │ │ │ ├── VehicleService.java │ │ │ │ ├── DispatchService.java │ │ │ │ └── DispatchVehicleService.java │ │ │ │ ├── config │ │ │ │ ├── WebConfig.java │ │ │ │ └── DataInitializerConfig.java │ │ │ │ └── controller │ │ │ │ ├── CustomerController.java │ │ │ │ ├── BranchController.java │ │ │ │ ├── VehicleController.java │ │ │ │ ├── DispatchController.java │ │ │ │ └── DispatchVehicleController.java │ │ └── resources │ │ │ └── application.yml │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── routing_backend │ │ └── RoutingBackendApplicationTests.java ├── docker-compose.yml ├── .gitignore ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── pom.xml ├── mvnw.cmd └── mvnw └── README.md /routing_frontend/img/branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endmr11/routing_app/HEAD/routing_frontend/img/branch.png -------------------------------------------------------------------------------- /routing_frontend/img/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endmr11/routing_app/HEAD/routing_frontend/img/marker.png -------------------------------------------------------------------------------- /routing_frontend/img/marker-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endmr11/routing_app/HEAD/routing_frontend/img/marker-green.png -------------------------------------------------------------------------------- /routing_frontend/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/enums/DeliveryRange.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.enums; 2 | 3 | public enum DeliveryRange { 4 | MORNING, 5 | MIDMORNING, 6 | AFTERNOON, 7 | EVENING 8 | } 9 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSSummary.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ORSSummary { 7 | 8 | private double distance; 9 | private double duration; 10 | } 11 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSEngine.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ORSEngine { 7 | 8 | private String version; 9 | private String build_date; 10 | private String graph_date; 11 | } 12 | -------------------------------------------------------------------------------- /routing_frontend/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /routing_frontend/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/repository/VehicleRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.repository; 2 | 3 | import com.example.routing_backend.entity.Vehicle; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface VehicleRepository extends JpaRepository { 7 | } -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSSegment.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | @Data 8 | public class ORSSegment { 9 | 10 | private double distance; 11 | private double duration; 12 | private List steps; 13 | } 14 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSQuery.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | @Data 8 | public class ORSQuery { 9 | 10 | private List> coordinates; 11 | private String profile; 12 | private String format; 13 | } 14 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/repository/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.repository; 2 | 3 | import com.example.routing_backend.entity.Customer; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface CustomerRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/repository/DispatchRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.repository; 2 | 3 | import com.example.routing_backend.entity.Dispatch; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface DispatchRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/repository/BranchRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.repository; 2 | 3 | import com.example.routing_backend.entity.Branch; 4 | 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface BranchRepository extends JpaRepository { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/opt/distancematrix/DistanceMatrix.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.opt.distancematrix; 2 | 3 | import java.time.Duration; 4 | import java.util.List; 5 | 6 | public record DistanceMatrix(List> entries) { 7 | 8 | public record Entry(Duration time, Distance distance) { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /routing_backend/src/test/java/com/example/routing_backend/RoutingBackendApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class RoutingBackendApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSMetadata.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ORSMetadata { 7 | 8 | private String attribution; 9 | private String service; 10 | private long timestamp; 11 | private ORSQuery query; 12 | private ORSEngine engine; 13 | } 14 | -------------------------------------------------------------------------------- /routing_frontend/.idea/routing_frontend.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/opt/distancematrix/OptCoordinates.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.opt.distancematrix; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Locale; 5 | 6 | public record OptCoordinates(BigDecimal lat, BigDecimal lon) { 7 | 8 | public String key() { 9 | return String.format(Locale.ENGLISH, "%s,%s", lat, lon); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/repository/DispatchVehicleRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.repository; 2 | 3 | import com.example.routing_backend.entity.DispatchVehicle; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface DispatchVehicleRepository extends JpaRepository { 7 | DispatchVehicle findByVehicleId(Long id); 8 | } -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSDirectionRequest.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.List; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class ORSDirectionRequest { 13 | 14 | private List> coordinates; 15 | } 16 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSRoute.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | @Data 8 | public class ORSRoute { 9 | 10 | private ORSSummary summary; 11 | private List segments; 12 | private List bbox; 13 | private String geometry; 14 | private List way_points; 15 | } 16 | -------------------------------------------------------------------------------- /routing_backend/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8070 3 | 4 | spring: 5 | datasource: 6 | driver-class-name: org.postgresql.Driver 7 | url: jdbc:postgresql://localhost:5432/routing 8 | username: username 9 | password: password 10 | 11 | jpa: 12 | hibernate: 13 | ddl-auto: create 14 | database: postgresql 15 | database-platform: org.hibernate.dialect.PostgreSQLDialect -------------------------------------------------------------------------------- /routing_backend/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | postgresql: 5 | image: postgres:latest 6 | container_name: postgresql 7 | ports: 8 | - "5432:5432" 9 | environment: 10 | POSTGRES_USER: username 11 | POSTGRES_PASSWORD: password 12 | PGDATA: /var/lib/postgresql/data/pgdata 13 | volumes: 14 | - postgresql-data:/var/lib/postgresql/data 15 | 16 | volumes: 17 | postgresql-data: -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSDirectionResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public class ORSDirectionResponse { 11 | 12 | private List bbox; 13 | private List routes; 14 | } 15 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/opt/distancematrix/Distance.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.opt.distancematrix; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public record Distance(BigDecimal meters) { 6 | 7 | public static final Distance ZERO = new Distance(BigDecimal.ZERO); 8 | 9 | public static Distance ofMeters(double value) { 10 | return new Distance(BigDecimal.valueOf(value)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/opt/distancematrix/provider/DistanceProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.opt.distancematrix.provider; 2 | 3 | import com.example.routing_backend.opt.distancematrix.DistanceMatrix; 4 | import com.example.routing_backend.opt.distancematrix.OptCoordinates; 5 | 6 | public interface DistanceProvider { 7 | 8 | DistanceMatrix.Entry fetch(OptCoordinates from, OptCoordinates to); 9 | } 10 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/entity/Customer.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.*; 5 | 6 | @Data 7 | @Entity 8 | @Table(name = "customers") 9 | public class Customer { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | private String firstName; 16 | private String lastName; 17 | private String phone; 18 | } 19 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/dto/ORSStep.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public class ORSStep { 11 | 12 | private double distance; 13 | private double duration; 14 | private int type; 15 | private String instruction; 16 | private String name; 17 | private List way_points; 18 | } 19 | -------------------------------------------------------------------------------- /routing_backend/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/RoutingBackendApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.openfeign.EnableFeignClients; 6 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 7 | 8 | @EnableJpaAuditing 9 | @SpringBootApplication 10 | public class RoutingBackendApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(RoutingBackendApplication.class, args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/entity/Branch.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Data; 5 | 6 | @Entity 7 | @Data 8 | @Table(name = "branches") 9 | public class Branch { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | private String name; 16 | private double latitude; 17 | private double longitude; 18 | 19 | private double boundingBoxLatitude1; 20 | private double boundingBoxLongitude1; 21 | private double boundingBoxLatitude2; 22 | private double boundingBoxLongitude2; 23 | } 24 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/entity/DispatchVehicle.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.*; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @Entity 10 | @Table(name = "dispatch_vehicles") 11 | public class DispatchVehicle { 12 | 13 | @Id 14 | @GeneratedValue(strategy = GenerationType.IDENTITY) 15 | private Long id; 16 | 17 | @ManyToOne 18 | @JoinColumn(name = "vehicle_id") 19 | private Vehicle vehicle; 20 | 21 | @OneToMany 22 | @JoinColumn(name = "dispatch_id") 23 | private List dispatch; 24 | 25 | private Long routeDate; 26 | } 27 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/enums/VehicleType.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.enums; 2 | 3 | public enum VehicleType { 4 | PANELVAN(1), 5 | LORRY(2), 6 | TRUCK(3); 7 | 8 | private final int id; 9 | 10 | VehicleType(int id) { 11 | this.id = id; 12 | } 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | public static VehicleType fromId(int id) { 19 | for (VehicleType type : values()) { 20 | if (type.getId() == id) { 21 | return type; 22 | } 23 | } 24 | throw new IllegalArgumentException("No VehicleType with id " + id); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/enums/DispatchType.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.enums; 2 | 3 | public enum DispatchType { 4 | SACK(1), 5 | BAG(2), 6 | PARCEL(3), 7 | FILE(4); 8 | 9 | private final int id; 10 | 11 | DispatchType(int id) { 12 | this.id = id; 13 | } 14 | 15 | public int getId() { 16 | return id; 17 | } 18 | 19 | public static DispatchType fromId(int id) { 20 | for (DispatchType type : values()) { 21 | if (type.getId() == id) { 22 | return type; 23 | } 24 | } 25 | throw new IllegalArgumentException("ID'ye ait zimmet bulunamadı " + id); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/utility/BoundingBoxUtility.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.utility; 2 | 3 | public class BoundingBoxUtility { 4 | 5 | public static boolean isWithinBoundingBox( 6 | double pointLatitude, 7 | double pointLongitude, 8 | double latitude1, 9 | double longitude1, 10 | double latitude2, 11 | double longitude2 12 | ) { 13 | 14 | boolean isWithinLatitude = pointLatitude <= latitude1 && pointLatitude >= latitude2; 15 | boolean isWithinLongitude = pointLongitude >= longitude1 && pointLongitude <= longitude2; 16 | 17 | return isWithinLatitude && isWithinLongitude; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/service/BranchService.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.service; 2 | 3 | import com.example.routing_backend.entity.Branch; 4 | import com.example.routing_backend.repository.BranchRepository; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class BranchService { 13 | 14 | private final BranchRepository branchRepository; 15 | 16 | public List findAll() { 17 | return branchRepository.findAll(); 18 | } 19 | 20 | public Branch save(Branch branch) { 21 | return branchRepository.save(branch); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addCorsMappings(CorsRegistry registry) { 12 | registry.addMapping("/**") 13 | .allowedOrigins("http://127.0.0.1:5500") 14 | .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") 15 | .allowedHeaders("*") 16 | .allowCredentials(true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/service/CustomerService.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.service; 2 | 3 | import com.example.routing_backend.entity.Customer; 4 | 5 | import com.example.routing_backend.repository.CustomerRepository; 6 | 7 | import org.springframework.stereotype.Service; 8 | 9 | import lombok.RequiredArgsConstructor; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class CustomerService { 16 | 17 | private final CustomerRepository customerRepository; 18 | 19 | public List findAll() { 20 | return customerRepository.findAll(); 21 | } 22 | 23 | public Customer save(Customer customer) { 24 | return customerRepository.save(customer); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/entity/Vehicle.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.entity; 2 | 3 | import com.example.routing_backend.enums.DeliveryRange; 4 | import com.example.routing_backend.enums.VehicleType; 5 | import jakarta.persistence.*; 6 | import lombok.*; 7 | 8 | @Data 9 | @Entity 10 | @Table(name = "vehicles") 11 | public class Vehicle { 12 | 13 | @Id 14 | @GeneratedValue(strategy = GenerationType.IDENTITY) 15 | private Long id; 16 | 17 | @Enumerated(EnumType.STRING) 18 | private VehicleType vehicleType; 19 | 20 | private String licensePlate; 21 | 22 | @ManyToOne 23 | @JoinColumn(name = "branch_id") 24 | private Branch branch; 25 | 26 | //private Long departureTime; 27 | @Enumerated(EnumType.STRING) 28 | private DeliveryRange deliveryRange; 29 | } 30 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/controller/CustomerController.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.controller; 2 | 3 | import com.example.routing_backend.entity.Customer; 4 | import com.example.routing_backend.service.CustomerService; 5 | import org.springframework.web.bind.annotation.*; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @RestController 11 | @RequestMapping("/customers") 12 | @RequiredArgsConstructor 13 | public class CustomerController { 14 | 15 | private final CustomerService customerService; 16 | 17 | @GetMapping 18 | public List getAllCustomers() { 19 | return customerService.findAll(); 20 | } 21 | 22 | @PostMapping 23 | public Customer createCustomer(@RequestBody Customer customer) { 24 | return customerService.save(customer); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/service/VehicleService.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.service; 2 | 3 | import com.example.routing_backend.entity.Vehicle; 4 | 5 | import com.example.routing_backend.repository.VehicleRepository; 6 | 7 | import org.springframework.stereotype.Service; 8 | 9 | import lombok.RequiredArgsConstructor; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class VehicleService { 16 | 17 | private final VehicleRepository vehicleRepository; 18 | 19 | public Vehicle findById(Long id) { 20 | return vehicleRepository.findById(id).orElse(null); 21 | } 22 | 23 | public List findAll() { 24 | return vehicleRepository.findAll(); 25 | } 26 | 27 | public Vehicle save(Vehicle vehicle) { 28 | return vehicleRepository.save(vehicle); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/controller/BranchController.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.controller; 2 | 3 | import com.example.routing_backend.entity.Branch; 4 | import com.example.routing_backend.service.BranchService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | 11 | @RestController 12 | @RequestMapping("/branches") 13 | @RequiredArgsConstructor 14 | public class BranchController { 15 | 16 | private final BranchService branchService; 17 | 18 | @GetMapping 19 | public ResponseEntity> getAllBranches() { 20 | return ResponseEntity.ok(branchService.findAll()); 21 | } 22 | 23 | @PostMapping 24 | public ResponseEntity createBranch(@RequestBody Branch branch) { 25 | return ResponseEntity.ok(branchService.save(branch)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /routing_frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | Anasayfa 11 | 12 | 13 | 14 |
15 | 16 |

Lütfen işlem yapmak istediğiniz şubeyi seçiniz!

17 |
18 |
19 | 20 |
21 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/controller/VehicleController.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.controller; 2 | 3 | import com.example.routing_backend.entity.Vehicle; 4 | import com.example.routing_backend.service.VehicleService; 5 | import org.springframework.web.bind.annotation.*; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @RestController 11 | 12 | @RequestMapping("/vehicles") 13 | 14 | @RequiredArgsConstructor 15 | public class VehicleController { 16 | 17 | private final VehicleService vehicleService; 18 | 19 | @GetMapping 20 | public List getAllVehicles() { 21 | 22 | return vehicleService.findAll(); 23 | } 24 | 25 | @GetMapping("/{id}") 26 | public Vehicle getVehicleById(@PathVariable Long id) { 27 | 28 | return vehicleService.findById(id); 29 | } 30 | 31 | @PostMapping 32 | public Vehicle createVehicle(@RequestBody Vehicle vehicle) { 33 | 34 | return vehicleService.save(vehicle); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/service/DispatchService.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.service; 2 | 3 | import com.example.routing_backend.entity.Dispatch; 4 | 5 | import com.example.routing_backend.repository.DispatchRepository; 6 | 7 | import org.springframework.stereotype.Service; 8 | 9 | import lombok.RequiredArgsConstructor; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class DispatchService { 16 | 17 | private final DispatchRepository dispatchRepository; 18 | 19 | public List findAll() { 20 | return dispatchRepository.findAll(); 21 | } 22 | 23 | public Dispatch findById(Long id) { 24 | return dispatchRepository.findById(id).orElse(null); 25 | } 26 | 27 | public Dispatch save(Dispatch dispatch) { 28 | return dispatchRepository.save(dispatch); 29 | } 30 | 31 | public List bulkSave(List dispatches) { 32 | return dispatchRepository.saveAll(dispatches); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /routing_backend/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/entity/Dispatch.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.entity; 2 | 3 | import com.example.routing_backend.enums.DeliveryRange; 4 | import com.example.routing_backend.enums.DispatchType; 5 | import jakarta.persistence.*; 6 | import lombok.*; 7 | 8 | @Data 9 | @Entity 10 | @Table(name = "dispatches") 11 | public class Dispatch { 12 | 13 | @Id 14 | @GeneratedValue(strategy = GenerationType.IDENTITY) 15 | private Long id; 16 | 17 | @Enumerated(EnumType.STRING) 18 | private DispatchType dispatchType; 19 | 20 | private double weight; 21 | 22 | @ManyToOne 23 | @JoinColumn(name = "customer_id") 24 | private Customer customer; 25 | 26 | @Enumerated(EnumType.STRING) 27 | private DeliveryRange deliveryRange; 28 | 29 | @Column(nullable = true) 30 | private Long preferFirstDeliveryTime; 31 | @Column(nullable = true) 32 | private Long preferLastDeliveryTime; 33 | 34 | private double receiverLatitude; 35 | private double receiverLongitude; 36 | 37 | private String receiverAddress; 38 | } 39 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/controller/DispatchController.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.controller; 2 | 3 | import com.example.routing_backend.entity.Dispatch; 4 | import com.example.routing_backend.service.DispatchService; 5 | import org.springframework.web.bind.annotation.*; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @RestController 11 | @RequestMapping("/dispatches") 12 | @RequiredArgsConstructor 13 | public class DispatchController { 14 | 15 | private final DispatchService dispatchService; 16 | 17 | @GetMapping 18 | public List getAllDispatches() { 19 | return dispatchService.findAll(); 20 | } 21 | 22 | @GetMapping("/{id}") 23 | public Dispatch getDispatchById(@PathVariable Long id) { 24 | return dispatchService.findById(id); 25 | } 26 | 27 | @PostMapping 28 | public Dispatch createDispatch(@RequestBody Dispatch dispatch) { 29 | return dispatchService.save(dispatch); 30 | } 31 | 32 | @PostMapping("/bulk") 33 | public List createDispatches(@RequestBody List dispatches) { 34 | return dispatchService.bulkSave(dispatches); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /routing_frontend/pages/home/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | Anasayfa 10 | 11 | 12 | 13 |
14 |
15 | Zimmet 16 | Dağıtım Listesi 17 | Rotalama 18 |
19 |
20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/service/DispatchVehicleService.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.service; 2 | 3 | import com.example.routing_backend.entity.DispatchVehicle; 4 | 5 | import com.example.routing_backend.repository.DispatchVehicleRepository; 6 | 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | public class DispatchVehicleService { 13 | 14 | private final DispatchVehicleRepository dispatchVehicleRepository; 15 | 16 | public DispatchVehicleService( 17 | DispatchVehicleRepository dispatchVehicleRepository 18 | ) { 19 | this.dispatchVehicleRepository = dispatchVehicleRepository; 20 | } 21 | 22 | public List findAll() { 23 | return dispatchVehicleRepository.findAll(); 24 | } 25 | 26 | public DispatchVehicle findById(Long id) { 27 | return dispatchVehicleRepository.findById(id).orElse(null); 28 | } 29 | 30 | public DispatchVehicle save(DispatchVehicle dispatchVehicle) { 31 | return dispatchVehicleRepository.save(dispatchVehicle); 32 | } 33 | 34 | public void deleteById(Long id) { 35 | dispatchVehicleRepository.deleteById(id); 36 | } 37 | 38 | public DispatchVehicle findByVehicleId(Long id) { 39 | return dispatchVehicleRepository.findByVehicleId(id); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/utility/DeliveryRangeUtility.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.utility; 2 | 3 | import com.example.routing_backend.enums.DeliveryRange; 4 | 5 | public class DeliveryRangeUtility { 6 | 7 | public static boolean isInRange(DeliveryRange dispatch, DeliveryRange vehicle) { 8 | boolean isInRange = false; 9 | if (dispatch == DeliveryRange.MORNING && vehicle == DeliveryRange.MORNING) { 10 | isInRange = true; 11 | } else if (dispatch == DeliveryRange.MORNING && vehicle == DeliveryRange.MIDMORNING) { 12 | isInRange = true; 13 | } else if (dispatch == DeliveryRange.MIDMORNING && vehicle == DeliveryRange.MORNING) { 14 | isInRange = true; 15 | } else if (dispatch == DeliveryRange.MIDMORNING && vehicle == DeliveryRange.MIDMORNING) { 16 | isInRange = true; 17 | } else if (dispatch == DeliveryRange.AFTERNOON && vehicle == DeliveryRange.AFTERNOON) { 18 | isInRange = true; 19 | } else if (dispatch == DeliveryRange.AFTERNOON && vehicle == DeliveryRange.EVENING) { 20 | isInRange = true; 21 | } else if (dispatch == DeliveryRange.EVENING && vehicle == DeliveryRange.AFTERNOON) { 22 | isInRange = true; 23 | } else if (dispatch == DeliveryRange.EVENING && vehicle == DeliveryRange.EVENING) { 24 | isInRange = true; 25 | } 26 | return isInRange; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /routing_frontend/index.js: -------------------------------------------------------------------------------- 1 | async function fetchBranches() { 2 | try { 3 | const response = await fetch("http://localhost:8070/branches"); 4 | 5 | if (!response.ok) { 6 | throw new Error(await response.text()); 7 | } 8 | 9 | const branches = await response.json(); 10 | 11 | displayBranches(branches); 12 | } catch (error) { 13 | alert(`Şubeler listenirken hata oluştu: ${error.message}`); 14 | } 15 | } 16 | 17 | function displayBranches(branches) { 18 | const container = document.getElementById("branch-container"); 19 | 20 | container.innerHTML = ""; 21 | 22 | branches.forEach((branch, index) => { 23 | const card = document.createElement("div"); 24 | card.className = "col-sm-6 mb-3"; 25 | 26 | const cardTitle = document.createElement("h5"); 27 | cardTitle.className = "card-title"; 28 | 29 | cardTitle.textContent = `Şube Ad: ${branch.name}`; 30 | 31 | cardTitle.addEventListener("click", function () { 32 | handleBranchClick(branch); 33 | }); 34 | 35 | const cardBody = document.createElement("div"); 36 | cardBody.className = "card-body"; 37 | cardBody.appendChild(cardTitle); 38 | 39 | const cardDiv = document.createElement("div"); 40 | cardDiv.className = "card"; 41 | cardDiv.appendChild(cardBody); 42 | 43 | card.appendChild(cardDiv); 44 | 45 | container.appendChild(card); 46 | }); 47 | } 48 | 49 | function handleBranchClick(branch) { 50 | sessionStorage.setItem("selectedBranch", JSON.stringify(branch)); 51 | 52 | window.location.href = `pages/home/home.html`; 53 | } 54 | 55 | fetchBranches(); 56 | -------------------------------------------------------------------------------- /routing_frontend/pages/dispatch/dispatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | Zimmet 13 | 14 | 15 | 39 | 40 |
41 | Ekle + 42 |
43 |
44 |

Zimmetler

45 |
46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/opt/distancematrix/DistanceMatrixService.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.opt.distancematrix; 2 | 3 | import com.example.routing_backend.opt.distancematrix.provider.DistanceProvider; 4 | import io.vavr.Tuple; 5 | import io.vavr.Tuple3; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.time.Duration; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | @Slf4j 14 | @Service 15 | public class DistanceMatrixService { 16 | 17 | private final DistanceProvider distanceProvider; 18 | 19 | public DistanceMatrixService(DistanceProvider distanceProvider) { 20 | this.distanceProvider = distanceProvider; 21 | } 22 | 23 | public DistanceMatrix createDistanceMatrix(List coordinates) { 24 | 25 | var entries = coordinates.stream() 26 | .flatMap(from -> coordinates.stream() 27 | .map(to -> Tuple.of(from, to, getEntry(from, to)))) 28 | .toList(); 29 | 30 | return createDistanceMatrix(coordinates, entries); 31 | } 32 | 33 | private DistanceMatrix.Entry getEntry(OptCoordinates from, OptCoordinates to) { 34 | 35 | if (from.equals(to)) { 36 | return new DistanceMatrix.Entry(Duration.ZERO, Distance.ZERO); 37 | } 38 | 39 | return distanceProvider.fetch(from, to); 40 | } 41 | 42 | private DistanceMatrix createDistanceMatrix(List coordinates, List> entries) { 43 | 44 | var map = entries.stream() 45 | .collect(Collectors.toMap( 46 | t -> Tuple.of(t._1(), t._2()), 47 | Tuple3::_3 48 | )); 49 | 50 | var entriesOrdered = coordinates.stream() 51 | .map(from -> coordinates.stream() 52 | .map(to -> map.get(Tuple.of(from, to))) 53 | .toList()) 54 | .toList(); 55 | 56 | return new DistanceMatrix(entriesOrdered); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/utility/DateUtility.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.utility; 2 | 3 | import com.example.routing_backend.enums.DeliveryRange; 4 | 5 | import java.util.Date; 6 | import java.util.List; 7 | import java.time.LocalDateTime; 8 | import java.time.ZoneId; 9 | import java.util.Arrays; 10 | 11 | public class DateUtility { 12 | 13 | public static DeliveryRange getDeliveryRange(Long departureTime) { 14 | Date timeD = new Date(departureTime * 1000); 15 | if (timeD.getHours() >= 7 && timeD.getHours() < 13) { 16 | return DeliveryRange.MORNING; 17 | } else if (timeD.getHours() >= 10 && timeD.getHours() < 13) { 18 | return DeliveryRange.MIDMORNING; 19 | } else if (timeD.getHours() >= 13 && timeD.getHours() < 16) { 20 | return DeliveryRange.AFTERNOON; 21 | } else if (timeD.getHours() >= 13 && timeD.getHours() < 19) { 22 | return DeliveryRange.EVENING; 23 | } else { 24 | throw new IllegalArgumentException("Geçersiz saat: " + timeD.getHours()); 25 | } 26 | } 27 | 28 | public static List getDeliveryRangeHours(DeliveryRange deliveryRange) { 29 | 30 | LocalDateTime today = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0); 31 | 32 | long startTime; 33 | long endTime; 34 | 35 | switch (deliveryRange) { 36 | case MIDMORNING, MORNING -> { 37 | 38 | startTime = today.withHour(7).atZone(ZoneId.systemDefault()).toEpochSecond(); 39 | endTime = today.withHour(12).atZone(ZoneId.systemDefault()).toEpochSecond(); 40 | } 41 | case AFTERNOON, EVENING -> { 42 | 43 | startTime = today.withHour(13).atZone(ZoneId.systemDefault()).toEpochSecond(); 44 | endTime = today.withHour(18).atZone(ZoneId.systemDefault()).toEpochSecond(); 45 | } 46 | default -> 47 | throw new IllegalArgumentException("Geçersiz teslimat aralığı: " + deliveryRange); 48 | } 49 | 50 | return Arrays.asList(startTime, endTime); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /routing_frontend/pages/dispatch/dispatch.js: -------------------------------------------------------------------------------- 1 | async function fetchDispatches() { 2 | try { 3 | const response = await fetch("http://localhost:8070/dispatches"); 4 | 5 | if (!response.ok) { 6 | throw new Error(await response.text()); 7 | } 8 | 9 | const dispatches = await response.json(); 10 | 11 | displayDispatches(dispatches); 12 | } catch (error) { 13 | alert(`Zimmetler listenirken hata oluştu: ${error.message}`); 14 | } 15 | } 16 | 17 | function displayDispatches(dispatches) { 18 | const container = document.getElementById("dispatch-container"); 19 | 20 | container.innerHTML = ""; 21 | 22 | dispatches.forEach((dispatch) => { 23 | const card = document.createElement("div"); 24 | card.className = "col-sm-6 mb-3"; 25 | 26 | card.innerHTML = ` 27 |
28 |
29 |
Zimmet #${dispatch.id}
30 |

31 | Müşteri: ${dispatch.customer.firstName} ${ 32 | dispatch.customer.lastName 33 | } (${dispatch.customer.phone})
34 | Tercih Edilen Saatler: ${ 35 | dispatch.preferFirstDeliveryTime && 36 | dispatch.preferLastDeliveryTime 37 | ? `${new Date( 38 | dispatch.preferFirstDeliveryTime * 1000 39 | ).toLocaleTimeString()} ile ${new Date( 40 | dispatch.preferLastDeliveryTime * 1000 41 | ).toLocaleTimeString()} arası.
` 42 | : "Bulunmamakta.
" 43 | } 44 | Koordinat: (${dispatch.receiverLatitude}, ${ 45 | dispatch.receiverLongitude 46 | })
47 | Adres: ${dispatch.receiverAddress}
48 | 49 |

50 | Detay 53 |
54 |
55 | `; 56 | 57 | container.appendChild(card); 58 | }); 59 | } 60 | 61 | fetchDispatches(); 62 | -------------------------------------------------------------------------------- /routing_frontend/pages/dispatch-details/dispatch-details.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Zimmet Detay 7 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 44 |
45 |

Zimmet Detayı

46 |
47 |
48 |
Zimmet Detayı
49 |

50 |
51 |
52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/opt/distancematrix/provider/GraphHopperDistanceProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.opt.distancematrix.provider; 2 | 3 | import com.example.routing_backend.opt.distancematrix.DistanceMatrix; 4 | import com.example.routing_backend.opt.distancematrix.Distance; 5 | import com.example.routing_backend.opt.distancematrix.OptCoordinates; 6 | import com.graphhopper.GHRequest; 7 | import com.graphhopper.GraphHopper; 8 | import com.graphhopper.config.CHProfile; 9 | import com.graphhopper.config.Profile; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.nio.file.Paths; 13 | import java.time.Duration; 14 | import java.util.Locale; 15 | 16 | @Service 17 | public class GraphHopperDistanceProvider implements DistanceProvider { 18 | 19 | public static String DATA_DIR = ".data"; 20 | public static String PBF_FILE_NAME = "turkey-latest.osm.pbf"; 21 | 22 | private final GraphHopper graphHopper; 23 | 24 | public GraphHopperDistanceProvider() { 25 | this.graphHopper = createGraphHopperInstance(); 26 | } 27 | 28 | private static GraphHopper createGraphHopperInstance() { 29 | var hopper = new GraphHopper(); 30 | hopper.setOSMFile(Paths.get(DATA_DIR, PBF_FILE_NAME).toString()); 31 | hopper.setGraphHopperLocation(".cache/routing-graph"); 32 | hopper.setProfiles(new Profile("car").setVehicle("car").setWeighting("fastest").setTurnCosts(false)); 33 | hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car")); 34 | hopper.importOrLoad(); 35 | return hopper; 36 | } 37 | 38 | @Override 39 | public DistanceMatrix.Entry fetch(OptCoordinates from, OptCoordinates to) { 40 | var ghRequest = new GHRequest( 41 | from.lat().doubleValue(), 42 | from.lon().doubleValue(), 43 | to.lat().doubleValue(), 44 | to.lon().doubleValue()) 45 | .setProfile("car") 46 | .setLocale(Locale.ENGLISH); 47 | var ghResponse = graphHopper.route(ghRequest); 48 | 49 | if (ghResponse.hasErrors()) { 50 | throw new IllegalStateException(ghResponse.getErrors().toString()); 51 | } 52 | 53 | var path = ghResponse.getBest(); 54 | var distanceInMeters = path.getDistance(); 55 | var timeInMs = path.getTime(); 56 | return new DistanceMatrix.Entry(Duration.ofMillis(timeInMs), Distance.ofMeters(distanceInMeters)); 57 | } 58 | 59 | public GraphHopper getGraphHopper() { 60 | return graphHopper; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /routing_frontend/pages/routing/routing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | Rota Olustur 17 | 18 | 19 | 43 |
44 |

Rota Olusturma

45 |
46 |
47 | 48 | 49 |
50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /routing_frontend/pages/dispatch-distribution/dispatch-distribution.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | Dağıtım Listesi Oluştur 15 | 16 | 17 | 41 |
42 |

Dağıtım Listesi Oluşturma

43 | 44 |
45 |
46 | 47 | 48 |
49 | 50 |
51 | 52 |
53 | Sadece saat aralığı olanlar 54 | 55 |
56 | 57 |
58 |
59 | 60 | -------------------------------------------------------------------------------- /routing_frontend/pages/dispatch-details/dispatch-details.js: -------------------------------------------------------------------------------- 1 | function getQueryParams() { 2 | const params = new URLSearchParams(window.location.search); 3 | return { 4 | id: params.get("id"), 5 | }; 6 | } 7 | 8 | async function fetchDispatchById(id) { 9 | try { 10 | const response = await fetch(`http://localhost:8070/dispatches/${id}`); 11 | if (!response.ok) { 12 | throw new Error(await response.text()); 13 | } 14 | const dispatch = await response.json(); 15 | if (!dispatch) throw new Error("Dispatch not found!"); 16 | return dispatch; 17 | } catch (error) { 18 | alert(`Zimmet listelenirken hata oluştu: ${error.message}`); 19 | } 20 | } 21 | 22 | async function initialize() { 23 | const { id } = getQueryParams(); 24 | const dispatch = await fetchDispatchById(id); 25 | 26 | if (dispatch) { 27 | document.getElementById( 28 | "dispatch-title" 29 | ).textContent = `Zimmet #${dispatch.id}`; 30 | document.getElementById("dispatch-details").innerHTML = ` 31 | Zimmet Tipi: ${dispatchTypeEnumToText( 32 | dispatch.dispatchType 33 | )}
34 | Ağırlık: ${dispatch.weight} kg
35 | Tercih Edilen Saatler: ${ 36 | dispatch.preferFirstDeliveryTime && dispatch.preferLastDeliveryTime 37 | ? `${new Date( 38 | dispatch.preferFirstDeliveryTime * 1000 39 | ).toLocaleTimeString()} ile ${new Date( 40 | dispatch.preferLastDeliveryTime * 1000 41 | ).toLocaleTimeString()} arası.
` 42 | : "Bulunmamakta.
" 43 | } 44 | Teslimat Aralığı: ${deliveryRangeEnumToText( 45 | dispatch.deliveryRange 46 | )}
47 | Müşteri: ${dispatch.customer.firstName} ${ 48 | dispatch.customer.lastName 49 | } (${dispatch.customer.phone})
50 | Koordinatlar: (${dispatch.receiverLatitude}, ${ 51 | dispatch.receiverLongitude 52 | })
53 | Adres: ${dispatch.receiverAddress} 54 | `; 55 | const map = L.map("map").setView( 56 | [dispatch.receiverLatitude, dispatch.receiverLongitude], 57 | 13 58 | ); 59 | L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { 60 | maxZoom: 19, 61 | }).addTo(map); 62 | const marker = L.marker([ 63 | dispatch.receiverLatitude, 64 | dispatch.receiverLongitude, 65 | ]) 66 | .addTo(map, { 67 | icon: L.icon({ 68 | iconUrl: "../../img/marker.png", 69 | iconSize: [50, 50], 70 | }), 71 | }) 72 | .bindPopup( 73 | `Detay: ${dispatch.customer.firstName} ${dispatch.customer.lastName}
Koordinatlar: ${dispatch.receiverLatitude}, ${dispatch.receiverLongitude}` 74 | ) 75 | .openPopup(); 76 | } else { 77 | document.getElementById("dispatch-details").textContent = 78 | "Zimmet Bulunamadı."; 79 | } 80 | } 81 | 82 | initialize(); 83 | -------------------------------------------------------------------------------- /routing_frontend/helper.js: -------------------------------------------------------------------------------- 1 | function vehicleTypeEnumToText(vehicleEnum) { 2 | switch (vehicleEnum) { 3 | case "PANELVAN": 4 | return "Panelvan"; 5 | case "LORRY": 6 | return "Kamyon"; 7 | case "TRUCK": 8 | return "Tır"; 9 | default: 10 | return "Araç Tipi Bulunamadı"; 11 | } 12 | } 13 | 14 | function dispatchTypeEnumToText(dispatchTypeEnum) { 15 | switch (dispatchTypeEnum) { 16 | case "SACK": 17 | return "Çuval"; 18 | case "BAG": 19 | return "Torba"; 20 | case "PARCEL": 21 | return "Koli"; 22 | case "FILE": 23 | return "Dosya"; 24 | default: 25 | return "Dosya Tipi Bulunamadı"; 26 | } 27 | } 28 | 29 | function deliveryRangeEnumToText(deliveryRangeEnum) { 30 | switch (deliveryRangeEnum) { 31 | case "MORNING": 32 | return "Sabah"; 33 | case "MIDMORNING": 34 | return "Öğleden Önce"; 35 | case "AFTERNOON": 36 | return "Öğleden Sonra"; 37 | case "EVENING": 38 | return "Akşam"; 39 | default: 40 | return "Teslimat Aralığı Bulunamadı"; 41 | } 42 | } 43 | 44 | function deliveryRangeTextToEnum(deliveryRangeEnum) { 45 | switch (deliveryRangeEnum) { 46 | case "Sabah": 47 | return "MORNING"; 48 | case "Öğleden Önce": 49 | return "MIDMORNING"; 50 | case "Öğleden Sonra": 51 | return "AFTERNOON"; 52 | case "Akşam": 53 | return "EVENING"; 54 | } 55 | } 56 | 57 | function isWithinBoundingBox( 58 | pointLatitude, 59 | pointLongitude, 60 | latitude1, 61 | longitude1, 62 | latitude2, 63 | longitude2 64 | ) { 65 | var isWithinLatitude = 66 | pointLatitude <= latitude1 && pointLatitude >= latitude2; 67 | var isWithinLongitude = 68 | pointLongitude >= longitude1 && pointLongitude <= longitude2; 69 | 70 | return isWithinLatitude && isWithinLongitude; 71 | } 72 | 73 | function getDeliveryRangeHours(deliveryRange) { 74 | switch (deliveryRange) { 75 | case "MIDMORNING": 76 | case "MORNING": 77 | return [7, 12]; 78 | case "AFTERNOON": 79 | case "EVENING": 80 | return [13, 18]; 81 | default: 82 | return [0, 0]; 83 | } 84 | } 85 | 86 | function createDateWithTime(hour, duration) { 87 | const now = new Date(); 88 | const year = now.getFullYear(); 89 | const month = now.getMonth(); 90 | const day = now.getDate(); 91 | 92 | const customDate = new Date(year, month, day, hour, 0); 93 | 94 | customDate.setMinutes(customDate.getMinutes() + duration); 95 | 96 | const formattedDay = day.toString().padStart(2, "0"); 97 | const formattedMonth = (month + 1).toString().padStart(2, "0"); 98 | const formattedHour = customDate.getHours().toString().padStart(2, "0"); 99 | const formattedMinute = customDate.getMinutes().toString().padStart(2, "0"); 100 | 101 | return `${formattedDay}.${formattedMonth}.${year} ${formattedHour}:${formattedMinute}:00`; 102 | } 103 | 104 | function isExistPreferedDeliveryTime(dispatch) { 105 | return dispatch?.preferFirstDeliveryTime && dispatch?.preferLastDeliveryTime; 106 | } 107 | -------------------------------------------------------------------------------- /routing_backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.3.4 9 | 10 | 11 | com.example 12 | routing_backend 13 | 0.0.1-SNAPSHOT 14 | routing_backend 15 | routing_backend 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 17 31 | 2023.0.3 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-data-jpa 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | 42 | 43 | org.postgresql 44 | postgresql 45 | runtime 46 | 47 | 48 | com.graphhopper 49 | jsprit-core 50 | 1.8 51 | 52 | 53 | com.graphhopper 54 | graphhopper-core 55 | 6.2 56 | 57 | 58 | io.vavr 59 | vavr 60 | 0.10.4 61 | 62 | 63 | org.springframework.cloud 64 | spring-cloud-starter-openfeign 65 | 66 | 67 | org.projectlombok 68 | lombok 69 | true 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-test 74 | test 75 | 76 | 77 | 78 | 79 | 80 | org.springframework.cloud 81 | spring-cloud-dependencies 82 | ${spring-cloud.version} 83 | pom 84 | import 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-maven-plugin 94 | 95 | 96 | 97 | org.projectlombok 98 | lombok 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /routing_frontend/pages/add-dispatch/add-dispatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | Zimmet Ekle 18 | 19 | 20 | 44 |
45 |

Zimmet Ekle

46 |
47 |
48 | 49 | 55 |
56 | 57 |
58 | 59 | 60 |
61 | 62 |
63 | 64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 |
72 | 73 | 79 |
80 | 81 |
82 | 83 |
84 |

85 | Enlem: Seçilmedi | Boylam: 86 | Seçilmedi | Adres: 87 | Seçilmedi 88 |

89 |
90 | 91 | 92 |
93 |
94 | 95 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/config/DataInitializerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.config; 2 | 3 | import com.example.routing_backend.entity.Branch; 4 | import com.example.routing_backend.entity.Customer; 5 | import com.example.routing_backend.entity.Dispatch; 6 | import com.example.routing_backend.entity.Vehicle; 7 | import com.example.routing_backend.enums.DeliveryRange; 8 | import com.example.routing_backend.enums.DispatchType; 9 | import com.example.routing_backend.enums.VehicleType; 10 | import com.example.routing_backend.repository.BranchRepository; 11 | import com.example.routing_backend.repository.CustomerRepository; 12 | import com.example.routing_backend.repository.DispatchRepository; 13 | import com.example.routing_backend.repository.VehicleRepository; 14 | import com.example.routing_backend.utility.DateUtility; 15 | import lombok.RequiredArgsConstructor; 16 | import org.bouncycastle.crypto.agreement.srp.SRP6Client; 17 | import org.springframework.beans.factory.annotation.Value; 18 | import org.springframework.boot.ApplicationRunner; 19 | import org.springframework.context.annotation.Bean; 20 | import org.springframework.context.annotation.Configuration; 21 | 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | @Configuration 27 | @RequiredArgsConstructor 28 | public class DataInitializerConfig { 29 | 30 | private final BranchRepository branchRepository; 31 | private final VehicleRepository vehicleRepository; 32 | private final CustomerRepository customerRepository; 33 | private final DispatchRepository dispatchRepository; 34 | 35 | @Value("${spring.jpa.hibernate.ddl-auto}") 36 | private String ddlAuto; 37 | 38 | @Bean 39 | public ApplicationRunner dataInitializer() { 40 | return args -> { 41 | if (ddlAuto.equals("create")) { 42 | Branch branch = new Branch(); 43 | branch.setName("Example Branch"); 44 | branch.setLatitude(41.1001871); 45 | branch.setLongitude(28.8892948); 46 | branch.setBoundingBoxLatitude1(41.1189); 47 | branch.setBoundingBoxLongitude1(28.8096); 48 | branch.setBoundingBoxLatitude2(41.0710); 49 | branch.setBoundingBoxLongitude2(28.8922); 50 | branchRepository.save(branch); 51 | 52 | Vehicle vehicle = new Vehicle(); 53 | vehicle.setBranch(branch); 54 | vehicle.setVehicleType(VehicleType.TRUCK); 55 | vehicle.setLicensePlate("34ABC123"); 56 | vehicle.setDeliveryRange(DateUtility.getDeliveryRange(1735645406L)); 57 | vehicleRepository.save(vehicle); 58 | 59 | Vehicle vehicle1 = new Vehicle(); 60 | vehicle1.setBranch(branch); 61 | vehicle1.setVehicleType(VehicleType.TRUCK); 62 | vehicle1.setLicensePlate("34ABC223"); 63 | vehicle1.setDeliveryRange(DateUtility.getDeliveryRange(1735617926L)); 64 | vehicleRepository.save(vehicle1); 65 | for (int i = 0; i < 5; i++) { 66 | Customer customer = new Customer(); 67 | customer.setFirstName("Name" + i); 68 | customer.setLastName("Surname" + i); 69 | customer.setPhone("5555555555"); 70 | customerRepository.save(customer); 71 | } 72 | 73 | List> coordinates = List.of( 74 | new HashMap<>(Map.of("latitude", 41.09768333375933, "longitude", 28.88992153956545)), 75 | new HashMap<>(Map.of("latitude", 41.1002360511431, "longitude", 28.89185302123544)), 76 | new HashMap<>(Map.of("latitude", 41.09779438743708, "longitude", 28.885973619325004)), 77 | new HashMap<>(Map.of("latitude", 41.10138408681063, "longitude", 28.880931066591593)), 78 | new HashMap<>(Map.of("latitude", 41.10196673483382, "longitude", 28.877557886397234)) 79 | ); 80 | 81 | for (int i = 0; i < 5; i++) { 82 | Dispatch dispatch = new Dispatch(); 83 | dispatch.setCustomer(customerRepository.findById((long) (i + 1)).orElse(null)); 84 | dispatch.setDispatchType(DispatchType.FILE); 85 | dispatch.setReceiverLatitude(coordinates.get(i).get("latitude")); 86 | dispatch.setReceiverLongitude(coordinates.get(i).get("longitude")); 87 | dispatch.setReceiverAddress("Sanko Park AVM"); 88 | dispatch.setDeliveryRange(DeliveryRange.EVENING); 89 | dispatch.setWeight(0.5); 90 | dispatchRepository.save(dispatch); 91 | } 92 | 93 | System.out.println("Başlangıç verileri başarıyla eklendi."); 94 | } 95 | 96 | }; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚚 Route Optimization Backend 2 | 3 | > A powerful Java Spring Boot backend for logistics and delivery route optimization. 4 | 5 | This project provides comprehensive solutions for vehicle routing, delivery scheduling, and optimization in logistics operations. 6 | 7 | ## 🌐 Related Projects 8 | 9 | - [Route Optimization iOS App](https://github.com/endmr11/routing_app_ios) 10 | 11 | ## ✨ Features 12 | 13 | - **🗺️ Vehicle Route Optimization**: Advanced VRP (Vehicle Routing Problem) solution using GraphHopper and Jsprit libraries 14 | - **⏰ Delivery Scheduling**: Time-window based delivery optimization for precise planning 15 | - **📍 Geographic Boundaries**: BoundingBox support for delivery zones and territories 16 | - **🔄 RESTful API**: Modern REST API endpoints for all operations 17 | 18 | ## 🛠️ Technologies 19 | 20 | | Technology | Purpose | 21 | |------------|---------| 22 | | Java 17 | Core programming language | 23 | | Spring Boot 3.3.4 | Application framework | 24 | | Spring Data JPA | Data access layer | 25 | | PostgreSQL | Database management | 26 | | Lombok | Boilerplate code reduction | 27 | | GraphHopper (Jsprit) | Route optimization engine | 28 | 29 | ## 🏗️ System Architecture 30 | 31 | The project consists of the following components: 32 | 33 | ### 📊 Entity Classes 34 | - `Branch`: Company branches with geographic coordinates 35 | - `Customer`: Customer information and delivery details 36 | - `Vehicle`: Delivery vehicles with capacity and type 37 | - `Dispatch`: Delivery tasks and assignments 38 | - `DispatchVehicle`: Vehicle-dispatch relationship mapping 39 | - `BoundingBox`: Geographic boundaries for delivery zones 40 | 41 | ### 💾 Repository Layer 42 | JPA-based data access layer providing basic CRUD operations for all entities. 43 | 44 | ### ⚙️ Service Layer 45 | Services that manage business logic: 46 | - `BranchService`: Branch management operations 47 | - `CustomerService`: Customer data management 48 | - `VehicleService`: Vehicle fleet management 49 | - `DispatchService`: Delivery task management 50 | - `DispatchVehicleService`: Vehicle-dispatch matching and route optimization 51 | 52 | ### 🧮 Optimization Modules 53 | - `OptRoutingProblem`: Route problem definition and constraints 54 | - `RoutingProblemSolver`: VRP solver implementation 55 | - `DistanceMatrixService`: Distance calculation between locations 56 | 57 | ### 🌐 Controller Layer 58 | Controllers providing RESTful APIs: 59 | - `BranchController`: `/branches` endpoints 60 | - `CustomerController`: `/customers` endpoints 61 | - `VehicleController`: `/vehicles` endpoints 62 | - `DispatchController`: `/dispatches` endpoints 63 | - `DispatchVehicleController`: `/dispatch-vehicles` endpoints 64 | 65 | ## 🚀 Installation 66 | 67 | ### Prerequisites 68 | - Java 17 or higher 69 | - PostgreSQL database 70 | - Maven 71 | 72 | ### Setup Steps 73 | 74 | 1. **Clone the repository:** 75 | ```bash 76 | git clone https://github.com/endmr11/routing_backend.git 77 | cd routing_backend 78 | ``` 79 | 80 | 2. **Configure the database:** 81 | - Create a PostgreSQL database named `routing` 82 | - Update the database configuration in `application.yml`: 83 | ```yaml 84 | spring: 85 | datasource: 86 | url: jdbc:postgresql://localhost:5432/routing 87 | username: [your-database-username] 88 | password: [your-database-password] 89 | ``` 90 | 91 | 3. **Build the project:** 92 | ```bash 93 | mvn clean install 94 | ``` 95 | 96 | 4. **Launch the application:** 97 | ```bash 98 | mvn spring-boot:run 99 | ``` 100 | 101 | 5. **Access the application:** 102 | The application will be available at http://localhost:8070 103 | 104 | ## 📡 API Usage 105 | 106 | ### Branch Management 107 | | Endpoint | Method | Description | 108 | |----------|--------|-------------| 109 | | `/branches` | GET | List all branches | 110 | | `/branches` | POST | Create a new branch | 111 | 112 | ### Customer Management 113 | | Endpoint | Method | Description | 114 | |----------|--------|-------------| 115 | | `/customers` | GET | List all customers | 116 | | `/customers` | POST | Create a new customer | 117 | 118 | ### Vehicle Management 119 | | Endpoint | Method | Description | 120 | |----------|--------|-------------| 121 | | `/vehicles` | GET | List all vehicles | 122 | | `/vehicles/{id}` | GET | Get vehicle by ID | 123 | | `/vehicles` | POST | Create a new vehicle | 124 | 125 | ### Dispatch Management 126 | | Endpoint | Method | Description | 127 | |----------|--------|-------------| 128 | | `/dispatches` | GET | List all dispatches | 129 | | `/dispatches/{id}` | GET | Get dispatch by ID | 130 | | `/dispatches` | POST | Create a new dispatch | 131 | | `/dispatches/bulk` | POST | Create multiple dispatches | 132 | 133 | ### Route Optimization 134 | | Endpoint | Method | Description | 135 | |----------|--------|-------------| 136 | | `/dispatch-vehicles` | GET | List all vehicle-dispatch mappings | 137 | | `/dispatch-vehicles/{id}` | GET | Get vehicle-dispatch by ID | 138 | | `/dispatch-vehicles` | POST | Create a new vehicle-dispatch mapping | 139 | | `/dispatch-vehicles/route/{id}` | GET | Calculate optimal route for a specific vehicle-dispatch mapping | 140 | 141 | ## 🧩 Optimization Algorithm 142 | 143 | The system implements a VRP (Vehicle Routing Problem) solution using the GraphHopper Jsprit library. The algorithm considers the following constraints: 144 | 145 | - ⏱️ Delivery time windows 146 | - 🗺️ Geographic delivery zones 147 | - 🚛 Vehicle capacities 148 | - 📏 Distance optimization 149 | 150 | ### Solution Process 151 | 152 | ``` 153 | ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ 154 | │ Create Problem │────▶│ Calculate │────▶│ Solve Using │ 155 | │ Definition │ │ Distance Matrix │ │ Jsprit Engine │ 156 | └─────────────────┘ └─────────────────┘ └─────────────────┘ 157 | │ │ 158 | │ ▼ 159 | ┌─────────────────┐ ┌─────────────────┐ 160 | │ Define │ │ Generate │ 161 | │ Constraints │ │ Optimal Routes │ 162 | └─────────────────┘ └─────────────────┘ 163 | ``` 164 | 165 | ## 📄 License 166 | 167 | This project is licensed under the MIT License - see the `LICENSE` file for details. 168 | -------------------------------------------------------------------------------- /routing_frontend/pages/dispatch-distribution/dispatch-distribution.js: -------------------------------------------------------------------------------- 1 | var selectedRange = null; 2 | 3 | var isFiltered = false; 4 | 5 | async function fetchVehicles() { 6 | try { 7 | const response = await fetch("http://localhost:8070/vehicles"); 8 | if (!response.ok) { 9 | throw new Error(await response.text()); 10 | } 11 | 12 | const vehicles = await response.json(); 13 | const vehicleSelect = document.getElementById("vehicle"); 14 | 15 | setRange(vehicles[0]?.deliveryRange); 16 | 17 | vehicles.forEach((vehicle) => { 18 | const option = document.createElement("option"); 19 | option.value = vehicle.id; 20 | option.textContent = deliveryRangeEnumToText(vehicle?.deliveryRange); 21 | 22 | option.label = `Araç Tipi: ${vehicleTypeEnumToText( 23 | vehicle.vehicleType 24 | )} | Plaka: ${ 25 | vehicle.licensePlate 26 | } | Araç Dağıtım Aralığı: ${deliveryRangeEnumToText( 27 | vehicle?.deliveryRange 28 | )}`; 29 | 30 | vehicleSelect.appendChild(option); 31 | }); 32 | } catch (error) { 33 | alert(`Araçlar listelenirken hata oluştu: ${error.message}`); 34 | } 35 | } 36 | 37 | document.getElementById("vehicle").addEventListener("change", async (event) => { 38 | const vehicleSelect = document.getElementById("vehicle"); 39 | var range = vehicleSelect.options[vehicleSelect.selectedIndex].text; 40 | setRange(deliveryRangeTextToEnum(range)); 41 | clearDispatches(); 42 | fetchDispatches(); 43 | }); 44 | 45 | function setRange(range) { 46 | console.log(range); 47 | selectedRange = range; 48 | } 49 | 50 | function clearDispatches() { 51 | const dispatchSelect = document.getElementById("dispatch"); 52 | while (dispatchSelect.options.length > 0) { 53 | dispatchSelect.remove(0); 54 | } 55 | } 56 | 57 | async function fetchDispatches() { 58 | try { 59 | const response = await fetch("http://localhost:8070/dispatches"); 60 | if (!response.ok) { 61 | throw new Error(await response.text()); 62 | } 63 | 64 | var dispatches = await response.json(); 65 | const dispatchSelect = document.getElementById("dispatch"); 66 | var filteredDispatches = []; 67 | 68 | dispatches.forEach((dispatch) => { 69 | console.log(dispatch.deliveryRange + " - " + selectedRange); 70 | 71 | if (dispatch.deliveryRange === selectedRange) { 72 | filteredDispatches.push(dispatch); 73 | } else if ( 74 | dispatch.deliveryRange === "MORNING" && 75 | selectedRange === "MORNING" 76 | ) { 77 | filteredDispatches.push(dispatch); 78 | } else if ( 79 | dispatch.deliveryRange === "MORNING" && 80 | selectedRange === "MIDMORNING" 81 | ) { 82 | filteredDispatches.push(dispatch); 83 | } else if ( 84 | dispatch.deliveryRange === "MIDMORNING" && 85 | selectedRange === "MORNING" 86 | ) { 87 | filteredDispatches.push(dispatch); 88 | } else if ( 89 | dispatch.deliveryRange === "MIDMORNING" && 90 | selectedRange === "MIDMORNING" 91 | ) { 92 | filteredDispatches.push(dispatch); 93 | } else if ( 94 | dispatch.deliveryRange === "AFTERNOON" && 95 | selectedRange === "AFTERNOON" 96 | ) { 97 | filteredDispatches.push(dispatch); 98 | } else if ( 99 | dispatch.deliveryRange === "AFTERNOON" && 100 | selectedRange === "EVENING" 101 | ) { 102 | filteredDispatches.push(dispatch); 103 | } else if ( 104 | dispatch.deliveryRange === "EVENING" && 105 | selectedRange === "AFTERNOON" 106 | ) { 107 | filteredDispatches.push(dispatch); 108 | } else if ( 109 | dispatch.deliveryRange === "EVENING" && 110 | selectedRange === "EVENING" 111 | ) { 112 | filteredDispatches.push(dispatch); 113 | } 114 | }); 115 | 116 | dispatches = []; 117 | filteredDispatches.forEach((dispatch) => { 118 | console.log(dispatch.deliveryRange); 119 | if (isFiltered) { 120 | if (isExistPreferedDeliveryTime(dispatch)) { 121 | dispatches.push(dispatch); 122 | } 123 | } 124 | }); 125 | 126 | (isFiltered ? dispatches : filteredDispatches).forEach((dispatch) => { 127 | const option = document.createElement("option"); 128 | option.value = dispatch.id; 129 | option.textContent = ` Müşteri: ${dispatch.customer.firstName} ${ 130 | dispatch.customer.lastName 131 | } | Saat Aralığı: ${ 132 | dispatch.preferFirstDeliveryTime && dispatch.preferLastDeliveryTime 133 | ? `${new Date( 134 | dispatch.preferFirstDeliveryTime * 1000 135 | ).toLocaleTimeString()} ile ${new Date( 136 | dispatch.preferLastDeliveryTime * 1000 137 | ).toLocaleTimeString()} arası.` 138 | : "Bulunmamakta." 139 | } | Teslimat Aralığı: ${deliveryRangeEnumToText( 140 | dispatch.deliveryRange 141 | )}`; 142 | 143 | dispatchSelect.appendChild(option); 144 | }); 145 | } catch (error) { 146 | alert(`Zimmetler listelenirken hata oluştu: ${error.message}`); 147 | } 148 | } 149 | 150 | document 151 | .getElementById("cargoForm") 152 | .addEventListener("submit", async (event) => { 153 | event.preventDefault(); 154 | 155 | const selectDispatch = document.getElementById("dispatch"); 156 | 157 | const selectedDispatches = Array.from(selectDispatch.selectedOptions).map( 158 | (option) => ({ 159 | id: parseInt(option.value), 160 | }) 161 | ); 162 | 163 | const data = { 164 | vehicle: { 165 | id: parseInt(document.getElementById("vehicle").value), 166 | }, 167 | dispatch: selectedDispatches, 168 | }; 169 | 170 | try { 171 | const response = await fetch("http://localhost:8070/dispatch-vehicles", { 172 | method: "POST", 173 | headers: { 174 | "Content-Type": "application/json", 175 | }, 176 | body: JSON.stringify(data), 177 | }); 178 | if (!response.ok) { 179 | throw new Error(await response.text()); 180 | } 181 | 182 | alert("Dağıtım oluşturma başarılı!"); 183 | document.getElementById("cargoForm").reset(); 184 | } catch (error) { 185 | alert(`Dağıtım oluşturulurken hata oluştu: ${error.message}`); 186 | } 187 | }); 188 | 189 | function filterByTime() { 190 | var checkBox = document.getElementById("myCheck"); 191 | isFiltered = checkBox.checked; 192 | clearDispatches(); 193 | fetchDispatches(); 194 | console.log("filterByTime" + isFiltered); 195 | } 196 | 197 | try { 198 | fetchVehicles().then(() => { 199 | fetchDispatches(); 200 | }); 201 | } catch (error) { 202 | alert(`Sayfa yüklenirken hata oluştu: ${error.message}`); 203 | } 204 | -------------------------------------------------------------------------------- /routing_backend/mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /routing_frontend/pages/routing/routing.js: -------------------------------------------------------------------------------- 1 | let map; 2 | 3 | var selectedBranch = JSON.parse(sessionStorage.getItem("selectedBranch")); 4 | 5 | let outboundPolyline = null; 6 | let returnPolyline = null; 7 | 8 | function showPosition(position) { 9 | map = L.map("map").setView( 10 | [position.coords.latitude, position.coords.longitude], 11 | 13 12 | ); 13 | 14 | L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { 15 | maxZoom: 19, 16 | attribution: 17 | '© OpenStreetMap contributors', 18 | }).addTo(map); 19 | 20 | drawBoundingBox(); 21 | } 22 | 23 | function drawBoundingBox() { 24 | L.rectangle( 25 | [ 26 | [ 27 | selectedBranch.boundingBoxLatitude1, 28 | selectedBranch.boundingBoxLongitude1, 29 | ], 30 | [ 31 | selectedBranch.boundingBoxLatitude2, 32 | selectedBranch.boundingBoxLongitude2, 33 | ], 34 | ], 35 | { 36 | color: "#ff7800", 37 | weight: 2, 38 | fillOpacity: 0.3, 39 | } 40 | ).addTo(map); 41 | } 42 | 43 | async function fetchDistributions() { 44 | try { 45 | const response = await fetch("http://localhost:8070/dispatch-vehicles"); 46 | if (!response.ok) { 47 | throw new Error(await response.text()); 48 | } 49 | const distributions = await response.json(); 50 | const distributionSelect = document.getElementById("distribution"); 51 | 52 | distributions.forEach((distribution) => { 53 | const option = document.createElement("option"); 54 | option.value = distribution.id; 55 | option.textContent = `Şube Adı: ${ 56 | distribution.vehicle.branch.name 57 | } | Araç Tipi: ${vehicleTypeEnumToText( 58 | distribution.vehicle.vehicleType 59 | )} | Araç Plakası: ${ 60 | distribution.vehicle.licensePlate 61 | } | Rota Saati: ${new Date( 62 | distribution.routeDate * 1000 63 | ).toLocaleTimeString( 64 | "tr-TR" 65 | )} | Araç Dağıtım Aralığı: ${deliveryRangeEnumToText( 66 | distribution.vehicle.deliveryRange 67 | )}`; 68 | distributionSelect.appendChild(option); 69 | }); 70 | } catch (error) { 71 | alert(`Dağıtımlar listelenirken hata oluştu: ${error.message}`); 72 | } 73 | } 74 | 75 | async function handleFormSubmit(event) { 76 | event.preventDefault(); 77 | const selectedDistributionId = document.getElementById("distribution").value; 78 | clearMap(); 79 | try { 80 | const response = await fetch( 81 | `http://localhost:8070/dispatch-vehicles/route/${selectedDistributionId}` 82 | ); 83 | if (!response.ok) { 84 | throw new Error(await response.text()); 85 | } 86 | const res = await response.json(); 87 | const vehicle = res.vehicle; 88 | const dispatches = res.dispatches; 89 | const routeGeometry = res.geometry; 90 | const totalDurationMinutes = res.totalDurationMinutes; 91 | const totalDurationFormatted = res.totalDurationFormatted; 92 | const route = res.routes[0]; 93 | const services = route.services; 94 | const totalDistance = res.totalDistance / 1000; 95 | 96 | services.forEach((service, index) => { 97 | const dispatch = dispatches.find( 98 | (d) => d.receiverLatitude == service.location.latitude 99 | ); 100 | const customer = dispatch?.customer; 101 | var arrivalTime = service.arrivalTime; 102 | var departureTime = service.endTime; 103 | 104 | L.marker([service.location.latitude, service.location.longitude], { 105 | icon: L.icon({ 106 | iconUrl: isExistPreferedDeliveryTime(dispatch) 107 | ? "../../img/marker-green.png" 108 | : "../../img/marker.png", 109 | iconSize: [50, 50], 110 | }), 111 | }) 112 | .addTo(map) 113 | .bindTooltip(`${index + 1}`, { 114 | permanent: true, 115 | opacity: dispatch == null ? 0.0 : 1.0, 116 | direction: "bottom", 117 | className: "labelstyle", 118 | }) 119 | .bindPopup( 120 | `${index + 1}. Durak | ${customer?.firstName} ${ 121 | customer?.lastName 122 | } | ${deliveryRangeEnumToText( 123 | dispatch?.deliveryRange 124 | )} | Tercih Edilen Saatler: ${ 125 | dispatch.preferFirstDeliveryTime && dispatch.preferLastDeliveryTime 126 | ? `${new Date( 127 | dispatch.preferFirstDeliveryTime * 1000 128 | ).toLocaleTimeString()} ile ${new Date( 129 | dispatch.preferLastDeliveryTime * 1000 130 | ).toLocaleTimeString()} arası.
` 131 | : "Bulunmamakta.
" 132 | }| Tahmini Varis Zamani: ${arrivalTime} 133 | | Tahmini Ayrilis Zamani: ${departureTime}` 134 | ) 135 | .openPopup(); 136 | }); 137 | 138 | L.marker([vehicle?.branch?.latitude, vehicle?.branch?.longitude], { 139 | icon: L.icon({ 140 | iconUrl: "../../img/branch.png", 141 | iconSize: [50, 50], 142 | }), 143 | }) 144 | .addTo(map) 145 | .bindTooltip("", { 146 | permanent: true, 147 | opacity: 0.0, 148 | direction: "bottom", 149 | className: "labelstyle", 150 | }) 151 | .bindPopup( 152 | `${ 153 | vehicle?.branch?.name 154 | } | Araç Dağıtım Aralığı: ${deliveryRangeEnumToText( 155 | vehicle?.deliveryRange 156 | )} | Araç Dağıtım Saat Aralığı: ${ 157 | getDeliveryRangeHours(vehicle?.deliveryRange)[0] + 158 | ":00" + 159 | " ile " + 160 | getDeliveryRangeHours(vehicle?.deliveryRange)[1] + 161 | ":00" 162 | } | Tahmini Araç Dönüş Saati: ${createDateWithTime( 163 | getDeliveryRangeHours(vehicle?.deliveryRange)[0], 164 | totalDurationMinutes 165 | )}` 166 | ) 167 | .openPopup(); 168 | 169 | outboundPolyline = L.polyline( 170 | routeGeometry.outbound.map((coord) => [coord[1], coord[0]]), 171 | { 172 | color: "blue", 173 | weight: 6, 174 | } 175 | ).addTo(map); 176 | 177 | returnPolyline = L.polyline( 178 | routeGeometry.return.map((coord) => [coord[1], coord[0]]), 179 | { 180 | color: "purple", 181 | weight: 6, 182 | } 183 | ).addTo(map); 184 | 185 | const bounds = L.featureGroup([ 186 | outboundPolyline, 187 | returnPolyline, 188 | ]).getBounds(); 189 | map.fitBounds(bounds); 190 | alert( 191 | `Toplam Mesafe: ${totalDistance.toFixed( 192 | 1 193 | )} km \nSüre: ${totalDurationFormatted}` 194 | ); 195 | } catch (error) { 196 | alert(`Rota oluşturulurken hata oluştu: ${error.message}`); 197 | } 198 | } 199 | 200 | function clearMap() { 201 | map.eachLayer(function (layer) { 202 | if (layer instanceof L.Marker || layer instanceof L.Polyline) { 203 | map.removeLayer(layer); 204 | } 205 | }); 206 | outboundPolyline = null; 207 | returnPolyline = null; 208 | drawBoundingBox(); 209 | } 210 | 211 | document 212 | .getElementById("toggleOutbound") 213 | .addEventListener("click", function () { 214 | if (outboundPolyline) { 215 | if (map.hasLayer(outboundPolyline)) { 216 | map.removeLayer(outboundPolyline); 217 | } else { 218 | outboundPolyline.addTo(map); 219 | } 220 | } 221 | }); 222 | 223 | document.getElementById("toggleReturn").addEventListener("click", function () { 224 | if (returnPolyline) { 225 | if (map.hasLayer(returnPolyline)) { 226 | map.removeLayer(returnPolyline); 227 | } else { 228 | returnPolyline.addTo(map); 229 | } 230 | } 231 | }); 232 | 233 | document 234 | .getElementById("cargoForm") 235 | .addEventListener("submit", handleFormSubmit); 236 | 237 | try { 238 | showPosition({ 239 | coords: { 240 | latitude: selectedBranch.latitude, 241 | longitude: selectedBranch.longitude, 242 | }, 243 | }); 244 | fetchDistributions(); 245 | } catch (error) { 246 | alert("Hata: " + error); 247 | } 248 | -------------------------------------------------------------------------------- /routing_frontend/pages/add-dispatch/add-dispatch.js: -------------------------------------------------------------------------------- 1 | let selectedLatitude = null; 2 | let selectedLongitude = null; 3 | 4 | let selectedDeliveryRange = "MORNING"; 5 | 6 | let preferFirstDeliveryTime = null; 7 | let preferLastDeliveryTime = null; 8 | 9 | var selectedBranch = JSON.parse(sessionStorage.getItem("selectedBranch")); 10 | 11 | document.addEventListener("DOMContentLoaded", () => { 12 | const map = L.map("map").setView([41.098893, 28.893887], 12); 13 | 14 | L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { 15 | maxZoom: 19, 16 | attribution: "© OpenStreetMap contributors", 17 | }).addTo(map); 18 | 19 | const marker = L.marker([41.098893, 28.893887], { draggable: true }).addTo( 20 | map 21 | ); 22 | 23 | L.rectangle( 24 | [ 25 | [ 26 | selectedBranch.boundingBoxLatitude1, 27 | selectedBranch.boundingBoxLongitude1, 28 | ], 29 | [ 30 | selectedBranch.boundingBoxLatitude2, 31 | selectedBranch.boundingBoxLongitude2, 32 | ], 33 | ], 34 | { 35 | color: "#ff7800", 36 | weight: 2, 37 | fillOpacity: 0.3, 38 | } 39 | ).addTo(map); 40 | 41 | L.marker([selectedBranch?.latitude, selectedBranch?.longitude], { 42 | icon: L.icon({ 43 | iconUrl: "../../img/branch.png", 44 | iconSize: [50, 50], 45 | }), 46 | }) 47 | .addTo(map) 48 | .bindTooltip("", { 49 | permanent: true, 50 | opacity: 0.0, 51 | direction: "bottom", 52 | className: "labelstyle", 53 | }); 54 | 55 | marker.on("dragend", (event) => { 56 | const { lat, lng } = event.target.getLatLng(); 57 | selectedLatitude = lat; 58 | selectedLongitude = lng; 59 | 60 | document.getElementById("latitude").textContent = lat.toFixed(6); 61 | document.getElementById("longitude").textContent = lng.toFixed(6); 62 | try { 63 | fetch( 64 | `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}` 65 | ).then(async (response) => { 66 | if (!response.ok) { 67 | throw new Error(await response.text()); 68 | } 69 | response.json().then((address) => { 70 | document.getElementById("address").textContent = address.display_name; 71 | }); 72 | }); 73 | } catch (error) { 74 | alert(`Adres bilgisi alınırken hata oluştu: ${error.message}`); 75 | } 76 | }); 77 | 78 | fetchCustomers(); 79 | }); 80 | 81 | async function fetchCustomers() { 82 | try { 83 | const response = await fetch("http://localhost:8070/customers"); 84 | if (!response.ok) { 85 | throw new Error(await response.text()); 86 | } 87 | const customers = await response.json(); 88 | const customerSelect = document.getElementById("customer"); 89 | 90 | customers.forEach((customer) => { 91 | const option = document.createElement("option"); 92 | option.value = customer.id; 93 | option.textContent = `${customer.firstName} ${customer.lastName}`; 94 | customerSelect.appendChild(option); 95 | }); 96 | } catch (error) { 97 | alert(`Müşteriler listenirken hata oluştu: ${error.message}`); 98 | } 99 | } 100 | 101 | document 102 | .getElementById("deliveryRange") 103 | .addEventListener("change", async (event) => { 104 | selectedDeliveryRange = event.target.value; 105 | console.log(selectedDeliveryRange); 106 | }); 107 | 108 | document 109 | .getElementById("firstDate") 110 | .addEventListener("change", async (event) => { 111 | checkDate(); 112 | }); 113 | 114 | document 115 | .getElementById("lastDate") 116 | .addEventListener("change", async (event) => { 117 | checkDate(); 118 | }); 119 | 120 | function checkDate() { 121 | var firstDate = document.getElementById("firstDate").value; 122 | var lastDate = document.getElementById("lastDate").value; 123 | 124 | if (firstDate != "" && lastDate != "") { 125 | if (firstDate > lastDate) { 126 | alert("İlk tarih son tarihten büyük olamaz!"); 127 | document.getElementById("firstDate").value = ""; 128 | document.getElementById("lastDate").value = ""; 129 | } else { 130 | if (lastDate.slice(0, 2) - firstDate.slice(0, 2) < 1) { 131 | alert("Teslimat aralığı en az 1 saat olmalıdır!"); 132 | document.getElementById("firstDate").value = ""; 133 | document.getElementById("lastDate").value = ""; 134 | } else { 135 | var currentFirstDate = new Date(); 136 | currentFirstDate.setHours( 137 | firstDate.slice(0, 2), 138 | firstDate.slice(3, 5), 139 | 0, 140 | 0 141 | ); 142 | var newFirstDate = currentFirstDate; 143 | var currentLastDate = new Date(); 144 | currentLastDate.setHours( 145 | lastDate.slice(0, 2), 146 | lastDate.slice(3, 5), 147 | 0, 148 | 0 149 | ); 150 | var newLastDate = currentLastDate; 151 | 152 | preferFirstDeliveryTime = newFirstDate.getTime() / 1000; 153 | preferLastDeliveryTime = newLastDate.getTime() / 1000; 154 | 155 | if (firstDate.slice(0, 2) >= 7 && lastDate.slice(0, 2) < 10) { 156 | selectedDeliveryRange = "MORNING"; 157 | document.getElementById("deliveryRange").value = "MORNING"; 158 | } else if (firstDate.slice(0, 2) >= 10 && lastDate.slice(0, 2) < 13) { 159 | selectedDeliveryRange = "MIDMORNING"; 160 | document.getElementById("deliveryRange").value = "MIDMORNING"; 161 | } else if (firstDate.slice(0, 2) >= 13 && lastDate.slice(0, 2) < 16) { 162 | selectedDeliveryRange = "AFTERNOON"; 163 | document.getElementById("deliveryRange").value = "AFTERNOON"; 164 | } else if (firstDate.slice(0, 2) >= 16 && lastDate.slice(0, 2) < 19) { 165 | selectedDeliveryRange = "EVENING"; 166 | document.getElementById("deliveryRange").value = "EVENING"; 167 | } 168 | } 169 | } 170 | } 171 | } 172 | 173 | document 174 | .getElementById("cargoForm") 175 | .addEventListener("submit", async (event) => { 176 | event.preventDefault(); 177 | 178 | if (!selectedLatitude || !selectedLongitude) { 179 | alert("Lütfen haritadan konum seçiniz."); 180 | return; 181 | } 182 | 183 | const data = { 184 | dispatchType: document.getElementById("dispatchType").value, 185 | weight: parseFloat(document.getElementById("weight").value), 186 | customer: { 187 | id: parseInt(document.getElementById("customer").value), 188 | }, 189 | receiverLatitude: selectedLatitude, 190 | receiverLongitude: selectedLongitude, 191 | deliveryRange: selectedDeliveryRange, 192 | receiverAddress: document.getElementById("address").textContent, 193 | preferFirstDeliveryTime: preferFirstDeliveryTime, 194 | preferLastDeliveryTime: preferLastDeliveryTime, 195 | }; 196 | 197 | if ( 198 | isWithinBoundingBox( 199 | selectedLatitude, 200 | selectedLongitude, 201 | selectedBranch.boundingBoxLatitude1, 202 | selectedBranch.boundingBoxLongitude1, 203 | selectedBranch.boundingBoxLatitude2, 204 | selectedBranch.boundingBoxLongitude2 205 | ) === false 206 | ) { 207 | alert("Teslimat adresi şube sınırları içinde değil!"); 208 | return; 209 | } 210 | 211 | try { 212 | const response = await fetch("http://localhost:8060/dispatches", { 213 | method: "POST", 214 | headers: { 215 | "Content-Type": "application/json", 216 | }, 217 | body: JSON.stringify(data), 218 | }); 219 | 220 | if (!response.ok) { 221 | throw new Error(await response.text()); 222 | } 223 | 224 | alert("Zimmet başarılı bir şekilde eklendi!"); 225 | 226 | document.getElementById("cargoForm").reset(); 227 | document.getElementById("deliveryRange").value = "MORNING"; 228 | selectedDeliveryRange = "MORNING"; 229 | document.getElementById("firstDate").value = ""; 230 | document.getElementById("lastDate").value = ""; 231 | preferFirstDeliveryTime = null; 232 | preferLastDeliveryTime = null; 233 | document.getElementById("latitude").textContent = "Not Selected"; 234 | document.getElementById("longitude").textContent = "Not Selected"; 235 | } catch (error) { 236 | alert(`Zimmet eklenirken hata oluştu: ${error.message}`); 237 | } 238 | }); 239 | -------------------------------------------------------------------------------- /routing_backend/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Optional ENV vars 25 | # ----------------- 26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source 27 | # MVNW_REPOURL - repo url base for downloading maven distribution 28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output 30 | # ---------------------------------------------------------------------------- 31 | 32 | set -euf 33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x 34 | 35 | # OS specific support. 36 | native_path() { printf %s\\n "$1"; } 37 | case "$(uname)" in 38 | CYGWIN* | MINGW*) 39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" 40 | native_path() { cygpath --path --windows "$1"; } 41 | ;; 42 | esac 43 | 44 | # set JAVACMD and JAVACCMD 45 | set_java_home() { 46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched 47 | if [ -n "${JAVA_HOME-}" ]; then 48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 49 | # IBM's JDK on AIX uses strange locations for the executables 50 | JAVACMD="$JAVA_HOME/jre/sh/java" 51 | JAVACCMD="$JAVA_HOME/jre/sh/javac" 52 | else 53 | JAVACMD="$JAVA_HOME/bin/java" 54 | JAVACCMD="$JAVA_HOME/bin/javac" 55 | 56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then 57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 59 | return 1 60 | fi 61 | fi 62 | else 63 | JAVACMD="$( 64 | 'set' +e 65 | 'unset' -f command 2>/dev/null 66 | 'command' -v java 67 | )" || : 68 | JAVACCMD="$( 69 | 'set' +e 70 | 'unset' -f command 2>/dev/null 71 | 'command' -v javac 72 | )" || : 73 | 74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then 75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | # hash string like Java String::hashCode 82 | hash_string() { 83 | str="${1:-}" h=0 84 | while [ -n "$str" ]; do 85 | char="${str%"${str#?}"}" 86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) 87 | str="${str#?}" 88 | done 89 | printf %x\\n $h 90 | } 91 | 92 | verbose() { :; } 93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } 94 | 95 | die() { 96 | printf %s\\n "$1" >&2 97 | exit 1 98 | } 99 | 100 | trim() { 101 | # MWRAPPER-139: 102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. 103 | # Needed for removing poorly interpreted newline sequences when running in more 104 | # exotic environments such as mingw bash on Windows. 105 | printf "%s" "${1}" | tr -d '[:space:]' 106 | } 107 | 108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties 109 | while IFS="=" read -r key value; do 110 | case "${key-}" in 111 | distributionUrl) distributionUrl=$(trim "${value-}") ;; 112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; 113 | esac 114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" 115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" 116 | 117 | case "${distributionUrl##*/}" in 118 | maven-mvnd-*bin.*) 119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ 120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in 121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; 122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; 123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; 124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;; 125 | *) 126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 127 | distributionPlatform=linux-amd64 128 | ;; 129 | esac 130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" 131 | ;; 132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; 133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; 134 | esac 135 | 136 | # apply MVNW_REPOURL and calculate MAVEN_HOME 137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" 139 | distributionUrlName="${distributionUrl##*/}" 140 | distributionUrlNameMain="${distributionUrlName%.*}" 141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}" 142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" 143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" 144 | 145 | exec_maven() { 146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : 147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" 148 | } 149 | 150 | if [ -d "$MAVEN_HOME" ]; then 151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME" 152 | exec_maven "$@" 153 | fi 154 | 155 | case "${distributionUrl-}" in 156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; 157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; 158 | esac 159 | 160 | # prepare tmp dir 161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then 162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } 163 | trap clean HUP INT TERM EXIT 164 | else 165 | die "cannot create temp dir" 166 | fi 167 | 168 | mkdir -p -- "${MAVEN_HOME%/*}" 169 | 170 | # Download and Install Apache Maven 171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 172 | verbose "Downloading from: $distributionUrl" 173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 174 | 175 | # select .zip or .tar.gz 176 | if ! command -v unzip >/dev/null; then 177 | distributionUrl="${distributionUrl%.zip}.tar.gz" 178 | distributionUrlName="${distributionUrl##*/}" 179 | fi 180 | 181 | # verbose opt 182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' 183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v 184 | 185 | # normalize http auth 186 | case "${MVNW_PASSWORD:+has-password}" in 187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;; 188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; 189 | esac 190 | 191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then 192 | verbose "Found wget ... using wget" 193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" 194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then 195 | verbose "Found curl ... using curl" 196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" 197 | elif set_java_home; then 198 | verbose "Falling back to use Java to download" 199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" 200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" 201 | cat >"$javaSource" <<-END 202 | public class Downloader extends java.net.Authenticator 203 | { 204 | protected java.net.PasswordAuthentication getPasswordAuthentication() 205 | { 206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); 207 | } 208 | public static void main( String[] args ) throws Exception 209 | { 210 | setDefault( new Downloader() ); 211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); 212 | } 213 | } 214 | END 215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java 216 | verbose " - Compiling Downloader.java ..." 217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" 218 | verbose " - Running Downloader.java ..." 219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" 220 | fi 221 | 222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 223 | if [ -n "${distributionSha256Sum-}" ]; then 224 | distributionSha256Result=false 225 | if [ "$MVN_CMD" = mvnd.sh ]; then 226 | echo "Checksum validation is not supported for maven-mvnd." >&2 227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 228 | exit 1 229 | elif command -v sha256sum >/dev/null; then 230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then 231 | distributionSha256Result=true 232 | fi 233 | elif command -v shasum >/dev/null; then 234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then 235 | distributionSha256Result=true 236 | fi 237 | else 238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 240 | exit 1 241 | fi 242 | if [ $distributionSha256Result = false ]; then 243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 245 | exit 1 246 | fi 247 | fi 248 | 249 | # unzip and move 250 | if command -v unzip >/dev/null; then 251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" 252 | else 253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" 254 | fi 255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" 256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" 257 | 258 | clean || : 259 | exec_maven "$@" 260 | -------------------------------------------------------------------------------- /routing_backend/src/main/java/com/example/routing_backend/controller/DispatchVehicleController.java: -------------------------------------------------------------------------------- 1 | package com.example.routing_backend.controller; 2 | 3 | import com.example.routing_backend.entity.Branch; 4 | import com.example.routing_backend.entity.Dispatch; 5 | import com.example.routing_backend.entity.DispatchVehicle; 6 | import com.example.routing_backend.entity.Vehicle; 7 | import com.example.routing_backend.service.DispatchService; 8 | import com.example.routing_backend.service.DispatchVehicleService; 9 | import com.example.routing_backend.service.VehicleService; 10 | import com.example.routing_backend.utility.BoundingBoxUtility; 11 | import com.example.routing_backend.utility.DateUtility; 12 | 13 | import com.example.routing_backend.utility.DeliveryRangeUtility; 14 | import com.graphhopper.GHRequest; 15 | import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; 16 | import com.graphhopper.jsprit.core.util.VehicleRoutingTransportCostsMatrix; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.http.ResponseEntity; 20 | import org.springframework.web.bind.annotation.*; 21 | import lombok.RequiredArgsConstructor; 22 | 23 | import java.time.Instant; 24 | import java.time.ZoneId; 25 | import java.time.format.DateTimeFormatter; 26 | import java.util.*; 27 | import java.util.stream.Collectors; 28 | 29 | import com.graphhopper.jsprit.core.algorithm.PrettyAlgorithmBuilder; 30 | import com.graphhopper.jsprit.core.algorithm.SearchStrategy; 31 | import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; 32 | import com.graphhopper.jsprit.core.algorithm.acceptor.GreedyAcceptance; 33 | import com.graphhopper.jsprit.core.algorithm.listener.IterationStartsListener; 34 | import com.graphhopper.jsprit.core.algorithm.module.RuinAndRecreateModule; 35 | import com.graphhopper.jsprit.core.algorithm.recreate.InsertionBuilder; 36 | import com.graphhopper.jsprit.core.algorithm.recreate.RegretInsertion; 37 | import com.graphhopper.jsprit.core.algorithm.ruin.RadialRuinStrategyFactory; 38 | import com.graphhopper.jsprit.core.algorithm.ruin.RandomRuinStrategyFactory; 39 | import com.graphhopper.jsprit.core.algorithm.ruin.RuinStrategy; 40 | import com.graphhopper.jsprit.core.algorithm.ruin.distance.AvgServiceAndShipmentDistance; 41 | import com.graphhopper.jsprit.core.algorithm.selector.SelectBest; 42 | import com.graphhopper.jsprit.core.algorithm.state.StateManager; 43 | import com.graphhopper.jsprit.core.analysis.SolutionAnalyser; 44 | import com.graphhopper.jsprit.core.problem.Location; 45 | import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; 46 | import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; 47 | import com.graphhopper.jsprit.core.problem.job.Service; 48 | import com.graphhopper.jsprit.core.problem.solution.SolutionCostCalculator; 49 | import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; 50 | import com.graphhopper.jsprit.core.problem.vehicle.FiniteFleetManagerFactory; 51 | import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager; 52 | import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; 53 | import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; 54 | import com.graphhopper.jsprit.core.util.Solutions; 55 | import com.example.routing_backend.opt.distancematrix.DistanceMatrix; 56 | import com.example.routing_backend.opt.distancematrix.OptCoordinates; 57 | import com.example.routing_backend.opt.distancematrix.provider.GraphHopperDistanceProvider; 58 | 59 | import java.math.BigDecimal; 60 | 61 | @RestController 62 | @RequestMapping("/dispatch-vehicles") 63 | @RequiredArgsConstructor 64 | public class DispatchVehicleController { 65 | 66 | private final DispatchVehicleService dispatchVehicleService; 67 | private final DispatchService dispatchService; 68 | private final VehicleService vehicleService; 69 | private final Random random = new Random(); 70 | private final GraphHopperDistanceProvider graphHopperDistanceProvider; 71 | 72 | Logger logger = LoggerFactory.getLogger(DispatchVehicleController.class); 73 | 74 | @GetMapping 75 | public List getAllDispatchVehicles() { 76 | List dispatchVehicles = dispatchVehicleService.findAll(); 77 | dispatchVehicles.removeIf(d -> d.getDispatch().isEmpty()); 78 | return dispatchVehicles; 79 | } 80 | 81 | @GetMapping("/{id}") 82 | 83 | public DispatchVehicle getDispatchVehicle(@PathVariable Long id) { 84 | return dispatchVehicleService.findById(id); 85 | } 86 | 87 | @PostMapping 88 | public ResponseEntity createDispatchVehicle(@RequestBody DispatchVehicle dispatchVehicle) { 89 | List dispatch = new ArrayList(); 90 | Vehicle vehicle = vehicleService.findById(dispatchVehicle.getVehicle().getId()); 91 | DispatchVehicle existingDispatchVehicle = dispatchVehicleService.findByVehicleId(vehicle.getId()); 92 | if (existingDispatchVehicle != null) { 93 | long id = existingDispatchVehicle.getId(); 94 | dispatchVehicleService.deleteById(id); 95 | } 96 | dispatchVehicle.getDispatch().forEach(d -> { 97 | Dispatch newD = dispatchService.findById(d.getId()); 98 | boolean isWithinBoundingBox = BoundingBoxUtility.isWithinBoundingBox( 99 | newD.getReceiverLatitude(), 100 | newD.getReceiverLongitude(), 101 | vehicle.getBranch().getBoundingBoxLatitude1(), 102 | vehicle.getBranch().getBoundingBoxLongitude1(), 103 | vehicle.getBranch().getBoundingBoxLatitude2(), 104 | vehicle.getBranch().getBoundingBoxLongitude2() 105 | ); 106 | if (DeliveryRangeUtility.isInRange(newD.getDeliveryRange(), vehicle.getDeliveryRange()) && isWithinBoundingBox) { 107 | dispatch.add(newD); 108 | } 109 | }); 110 | if (dispatch.isEmpty()) { 111 | return ResponseEntity.badRequest().body("Zimmet bos olamaz"); 112 | } 113 | dispatchVehicle.setDispatch(dispatch); 114 | dispatchVehicle.setRouteDate(Instant.now().getEpochSecond()); 115 | return ResponseEntity.ok(dispatchVehicleService.save(dispatchVehicle)); 116 | } 117 | 118 | @GetMapping("/route/{id}") 119 | 120 | public ResponseEntity createRouting(@PathVariable Long id) { 121 | DispatchVehicle dispatchVehicle = dispatchVehicleService.findById(id); 122 | Vehicle vehicle = dispatchVehicle.getVehicle(); 123 | Branch branch = vehicle.getBranch(); 124 | List dispatchList = dispatchVehicle.getDispatch(); 125 | 126 | List locations = new ArrayList<>(); 127 | 128 | locations.add(Location.newInstance(branch.getLatitude(), branch.getLongitude())); 129 | 130 | dispatchList.forEach(d 131 | -> locations.add(Location.newInstance(d.getReceiverLatitude(), d.getReceiverLongitude())) 132 | ); 133 | logger.info("Locations: " + locations); 134 | 135 | double[][] distanceMatrix = new double[locations.size()][locations.size()]; 136 | double[][] timeMatrix = new double[locations.size()][locations.size()]; 137 | 138 | List optCoordinates = locations.stream() 139 | .map(location -> new OptCoordinates( 140 | BigDecimal.valueOf(location.getCoordinate().getX()), 141 | BigDecimal.valueOf(location.getCoordinate().getY()) 142 | )) 143 | .toList(); 144 | 145 | for (int i = 0; i < locations.size(); i++) { 146 | for (int j = 0; j < locations.size(); j++) { 147 | if (i != j) { 148 | OptCoordinates from = optCoordinates.get(i); 149 | OptCoordinates to = optCoordinates.get(j); 150 | 151 | DistanceMatrix.Entry entry = graphHopperDistanceProvider.fetch(from, to); 152 | 153 | distanceMatrix[i][j] = entry.distance().meters().doubleValue(); 154 | timeMatrix[i][j] = entry.time().getSeconds(); 155 | } 156 | } 157 | } 158 | 159 | VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); 160 | 161 | for (int i = 0; i < dispatchList.size(); i++) { 162 | Dispatch d = dispatchList.get(i); 163 | 164 | int serviceTime = (random.nextInt(10) + 1) * 60; 165 | 166 | if (d.getPreferFirstDeliveryTime() != null) { 167 | vrpBuilder.addJob(Service.Builder.newInstance(d.getId().toString()) 168 | .addSizeDimension(0, 1) 169 | .setLocation(locations.get(i + 1)) 170 | .setServiceTime(serviceTime) 171 | .addTimeWindow(d.getPreferFirstDeliveryTime(), d.getPreferLastDeliveryTime()) 172 | .build()); 173 | } else { 174 | vrpBuilder.addJob(Service.Builder.newInstance(d.getId().toString()) 175 | .addSizeDimension(0, 1) 176 | .setLocation(locations.get(i + 1)) 177 | .setServiceTime(serviceTime) 178 | .build()); 179 | } 180 | } 181 | 182 | VehicleTypeImpl vehicleType = VehicleTypeImpl.Builder.newInstance(vehicle.getBranch().getId().toString()) 183 | .addCapacityDimension(0, 9999) 184 | .setCostPerDistance(1.0) 185 | .setFixedCost(100) 186 | .build(); 187 | 188 | VehicleImpl.Builder vehicleBuilder = VehicleImpl.Builder.newInstance(vehicle.getId().toString()); 189 | vehicleBuilder.setStartLocation(locations.get(0)) 190 | .setType(vehicleType); 191 | 192 | List deliveryHours = DateUtility.getDeliveryRangeHours(vehicle.getDeliveryRange()); 193 | Long startTime = deliveryHours.get(0); 194 | Long endTime = deliveryHours.get(1); 195 | 196 | vehicleBuilder 197 | .setEarliestStart(startTime) 198 | .setLatestArrival(endTime); 199 | 200 | vrpBuilder.addVehicle(vehicleBuilder.build()); 201 | 202 | VehicleRoutingTransportCostsMatrix.Builder matrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true); 203 | for (int i = 0; i < locations.size(); i++) { 204 | for (int j = 0; j < locations.size(); j++) { 205 | matrixBuilder.addTransportTime(locations.get(i).getId(), locations.get(j).getId(), timeMatrix[i][j]); 206 | matrixBuilder.addTransportDistance(locations.get(i).getId(), locations.get(j).getId(), distanceMatrix[i][j]); 207 | } 208 | } 209 | vrpBuilder.setRoutingCost(matrixBuilder.build()); 210 | 211 | vrpBuilder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE); 212 | VehicleRoutingProblem vrp = vrpBuilder.build(); 213 | VehicleRoutingAlgorithm vra = createAlgorithm(vrp); 214 | vra.setMaxIterations(10); 215 | Collection solutions = vra.searchSolutions(); 216 | VehicleRoutingProblemSolution solution = Solutions.bestOf(solutions); 217 | 218 | Map response = new HashMap<>(); 219 | response.put("unassignedJobs", solution.getUnassignedJobs().stream() 220 | .map(job -> Map.of( 221 | "jobId", job.getId(), 222 | "location", Map.of( 223 | "latitude", ((Service) job).getLocation().getCoordinate().getX(), 224 | "longitude", ((Service) job).getLocation().getCoordinate().getY() 225 | ) 226 | )) 227 | .collect(Collectors.toList())); 228 | 229 | List> routes = new ArrayList<>(); 230 | List> coordinates = new ArrayList<>(); 231 | solution.getRoutes().forEach(route -> { 232 | Map routeInfo = new HashMap<>(); 233 | routeInfo.put("vehicleId", route.getVehicle().getId()); 234 | routeInfo.put("startTime", formatTimestamp(route.getStart().getEndTime())); 235 | 236 | List> services = new ArrayList<>(); 237 | route.getActivities().forEach(activity -> { 238 | Map service = new HashMap<>(); 239 | if (activity.getLocation() != null) { 240 | service.put("location", Map.of( 241 | "latitude", activity.getLocation().getCoordinate().getX(), 242 | "longitude", activity.getLocation().getCoordinate().getY() 243 | )); 244 | coordinates.add(Arrays.asList(activity.getLocation().getCoordinate().getY(), activity.getLocation().getCoordinate().getX())); 245 | } 246 | service.put("arrivalTime", formatTimestamp(activity.getArrTime())); 247 | service.put("endTime", formatTimestamp(activity.getEndTime())); 248 | service.put("duration", activity.getOperationTime()); 249 | services.add(service); 250 | }); 251 | routeInfo.put("services", services); 252 | routes.add(routeInfo); 253 | }); 254 | response.put("routes", routes); 255 | coordinates.add(0, List.of(branch.getLongitude(), branch.getLatitude())); 256 | coordinates.add(List.of(branch.getLongitude(), branch.getLatitude())); 257 | if (coordinates.size() < 3) { 258 | return ResponseEntity.badRequest().body("Dagitima ait zimmet bulunamadi!"); 259 | } else { 260 | Map>> routeGeometry = new HashMap<>(); 261 | List> outboundRoute = new ArrayList<>(); 262 | List> returnRoute = new ArrayList<>(); 263 | 264 | for (int i = 0; i < coordinates.size() - 1; i++) { 265 | List from = coordinates.get(i); 266 | List to = coordinates.get(i + 1); 267 | 268 | var ghRequest = new GHRequest( 269 | from.get(1), 270 | from.get(0), 271 | to.get(1), 272 | to.get(0) 273 | ) 274 | .setProfile("car") 275 | .setLocale(Locale.ENGLISH); 276 | 277 | var ghResponse = graphHopperDistanceProvider.getGraphHopper().route(ghRequest); 278 | 279 | if (ghResponse.hasErrors()) { 280 | throw new IllegalStateException(ghResponse.getErrors().toString()); 281 | } 282 | 283 | var pointList = ghResponse.getBest().getPoints(); 284 | List> segmentPoints = new ArrayList<>(); 285 | for (int j = 0; j < pointList.size(); j++) { 286 | segmentPoints.add(Arrays.asList( 287 | pointList.getLon(j), 288 | pointList.getLat(j) 289 | )); 290 | } 291 | 292 | if (i < coordinates.size() - 2) { 293 | outboundRoute.addAll(segmentPoints); 294 | } else { 295 | 296 | returnRoute.addAll(segmentPoints); 297 | } 298 | } 299 | routeGeometry.put("outbound", outboundRoute); 300 | routeGeometry.put("return", returnRoute); 301 | response.put("geometry", routeGeometry); 302 | } 303 | response.put("vehicle", vehicle); 304 | response.put("dispatches", dispatchList); 305 | response.put("totalDistance", Math.round(solution.getCost())); 306 | 307 | solution.getRoutes().forEach(route -> { 308 | double totalDuration = 0; 309 | TourActivity prevAct = route.getStart(); 310 | 311 | for (TourActivity act : route.getActivities()) { 312 | 313 | double travelTime = vrp.getTransportCosts().getTransportTime( 314 | prevAct.getLocation(), 315 | act.getLocation(), 316 | prevAct.getEndTime(), 317 | route.getDriver(), 318 | route.getVehicle() 319 | ); 320 | 321 | totalDuration += travelTime; 322 | 323 | totalDuration += act.getOperationTime(); 324 | 325 | prevAct = act; 326 | } 327 | 328 | totalDuration += vrp.getTransportCosts().getTransportTime( 329 | prevAct.getLocation(), 330 | route.getEnd().getLocation(), 331 | prevAct.getEndTime(), 332 | route.getDriver(), 333 | route.getVehicle() 334 | ); 335 | 336 | response.put("totalDurationMinutes", Math.round(totalDuration / 60.0)); 337 | 338 | long hours = (long) (totalDuration / 3600); 339 | long minutes = (long) ((totalDuration % 3600) / 60); 340 | response.put("totalDurationFormatted", String.format("%d saat %d dakika", hours, minutes)); 341 | }); 342 | 343 | return ResponseEntity.ok(response); 344 | } 345 | 346 | public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp) { 347 | VehicleFleetManager fleetManager = new FiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager(); 348 | StateManager stateManager = new StateManager(vrp); 349 | ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); 350 | InsertionBuilder iBuilder = new InsertionBuilder(vrp, fleetManager, stateManager, constraintManager); 351 | iBuilder.setInsertionStrategy(InsertionBuilder.Strategy.REGRET); 352 | RegretInsertion regret = (RegretInsertion) iBuilder.build(); 353 | RuinStrategy randomRuin = new RandomRuinStrategyFactory(0.5).createStrategy(vrp); 354 | RuinStrategy radialRuin = new RadialRuinStrategyFactory(0.3, new AvgServiceAndShipmentDistance(vrp.getTransportCosts())).createStrategy(vrp); 355 | SolutionCostCalculator objectiveFunction = getObjectiveFunction(vrp); 356 | SearchStrategy firstStrategy = new SearchStrategy("firstStrategy", new SelectBest(), new GreedyAcceptance(1), objectiveFunction); 357 | firstStrategy.addModule(new RuinAndRecreateModule("randRuinRegretIns", regret, randomRuin)); 358 | SearchStrategy secondStrategy = new SearchStrategy("secondStrategy", new SelectBest(), new GreedyAcceptance(1), objectiveFunction); 359 | secondStrategy.addModule(new RuinAndRecreateModule("radRuinRegretIns", regret, radialRuin)); 360 | SearchStrategy thirdStrategy = new SearchStrategy("thirdStrategy", new SelectBest(), new GreedyAcceptance(1), objectiveFunction); 361 | secondStrategy.addModule(new RuinAndRecreateModule("radRuinBestIns", regret, radialRuin)); 362 | PrettyAlgorithmBuilder prettyAlgorithmBuilder = PrettyAlgorithmBuilder.newInstance(vrp, fleetManager, stateManager, constraintManager); 363 | final VehicleRoutingAlgorithm vra = prettyAlgorithmBuilder 364 | .withStrategy(firstStrategy, 0.1).withStrategy(secondStrategy, 0.5).withStrategy(thirdStrategy, 0.5) 365 | .addCoreStateAndConstraintStuff() 366 | .constructInitialSolutionWith(regret, objectiveFunction) 367 | .build(); 368 | 369 | IterationStartsListener strategyAdaptor = new IterationStartsListener() { 370 | @Override 371 | public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) { 372 | if (i == 50) { 373 | vra.getSearchStrategyManager().informStrategyWeightChanged("firstStrategy", 0.0); 374 | } 375 | if (i == 90) { 376 | vra.getSearchStrategyManager().informStrategyWeightChanged("firstStrategy", 0.7); 377 | } 378 | } 379 | }; 380 | vra.addListener(strategyAdaptor); 381 | return vra; 382 | } 383 | 384 | private static SolutionCostCalculator getObjectiveFunction(final VehicleRoutingProblem vrp) { 385 | return solution -> { 386 | SolutionAnalyser analyser = new SolutionAnalyser(vrp, solution, (from, to, departureTime, vehicle) -> vrp.getTransportCosts().getTransportCost(from, to, 0., null, null)); 387 | return analyser.getVariableTransportCosts() + solution.getUnassignedJobs().size() * 500.; 388 | }; 389 | } 390 | 391 | private String formatTimestamp(double timestamp) { 392 | return Instant.ofEpochSecond((long) timestamp) 393 | .atZone(ZoneId.of("Europe/Istanbul")) 394 | .format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")); 395 | } 396 | } 397 | --------------------------------------------------------------------------------