├── .idea ├── .name ├── vcs.xml ├── .gitignore ├── encodings.xml ├── modules.xml ├── misc.xml ├── dataSources.xml ├── compiler.xml └── jarRepositories.xml ├── system.properties ├── src ├── main │ ├── java │ │ └── com │ │ │ └── uber │ │ │ └── api │ │ │ ├── models │ │ │ ├── CarType.java │ │ │ ├── Gender.java │ │ │ ├── BookingType.java │ │ │ ├── BookingStatus.java │ │ │ ├── PaymentGateway.java │ │ │ ├── Role.java │ │ │ ├── Color.java │ │ │ ├── Location.java │ │ │ ├── PaymentReceipt.java │ │ │ ├── Car.java │ │ │ ├── Account.java │ │ │ ├── Booking.java │ │ │ ├── Passenger.java │ │ │ ├── OTP.java │ │ │ ├── Driver.java │ │ │ └── Auditable.java │ │ │ ├── controllers │ │ │ ├── AuthController.java │ │ │ ├── DriverController.java │ │ │ ├── PassengerController.java │ │ │ └── DBInitController.java │ │ │ ├── exceptions │ │ │ ├── UberAPIException.java │ │ │ └── NoSuchUserException.java │ │ │ ├── repositories │ │ │ ├── BookingRepository.java │ │ │ ├── RoleRepository.java │ │ │ ├── AccountRepository.java │ │ │ ├── PassengerRepository.java │ │ │ └── DriverRepository.java │ │ │ ├── UberApiApplication.java │ │ │ ├── security │ │ │ └── UberSecurityConfig.java │ │ │ ├── services │ │ │ ├── BookingService.java │ │ │ └── TrackingService.java │ │ │ └── utils │ │ │ └── QuadTree.java │ └── resources │ │ └── application.properties └── test │ └── java │ └── com │ └── uber │ └── api │ └── UberApiApplicationTests.java ├── README.md ├── HELP.md ├── pom.xml ├── mvnw.cmd ├── mvnw └── api.iml /.idea/.name: -------------------------------------------------------------------------------- 1 | api -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=11 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/CarType.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | public enum CarType { 4 | XL, 5 | NotXL 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Gender.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | public enum Gender { 4 | MALE, 5 | FEMALE, 6 | OTHER 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/BookingType.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | public enum BookingType { 4 | XL, 5 | Prime, 6 | Go 7 | } 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/controllers/AuthController.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.controllers; 2 | 3 | public class AuthController { 4 | // login 5 | // logout 6 | // register various account types 7 | } 8 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/exceptions/UberAPIException.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.exceptions; 2 | 3 | public class UberAPIException extends RuntimeException { 4 | public UberAPIException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/java/com/uber/api/UberApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.uber.api; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class UberApiApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/repositories/BookingRepository.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.repositories; 2 | 3 | import com.uber.api.models.Booking; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface BookingRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/exceptions/NoSuchUserException.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.exceptions; 2 | 3 | public class NoSuchUserException extends UberAPIException { 4 | public NoSuchUserException(String message) { 5 | super(message); 6 | } 7 | } 8 | 9 | // exceptions you throw should be specific 10 | // your exceptions should be cachtable in a generic manner 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/BookingStatus.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | public enum BookingStatus { 4 | REQUESTED, // driver not assigned 5 | SCHEDULED, // driver is assigned, but for future 6 | CAB_ARRIVED, // driver has reached your place 7 | ONGOING, // ride is in progress 8 | COMPLETED, 9 | CANCELLED // passenger/driver cancelled the booking 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/PaymentGateway.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.Table; 7 | 8 | @Builder 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Entity 12 | @Table(name = "paymentgateway") 13 | @Getter 14 | @Setter 15 | public class PaymentGateway extends Auditable{ 16 | private String name; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | 4 | import lombok.*; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.Table; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Entity 13 | @Table(name = "role") 14 | @Getter 15 | @Setter 16 | public class Role extends Auditable{ 17 | private String name; 18 | private String description; 19 | } 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Color.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.Table; 11 | 12 | @Entity 13 | @Getter @Setter 14 | @Table(name="color") 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class Color extends Auditable { 18 | private String name; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/UberApiApplication.java: -------------------------------------------------------------------------------- 1 | package com.uber.api; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 6 | 7 | @SpringBootApplication 8 | @EnableJpaRepositories 9 | public class UberApiApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(UberApiApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Location.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | 4 | import lombok.*; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.Table; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Entity 13 | @Table(name = "location") 14 | @Getter 15 | @Setter 16 | public class Location extends Auditable { 17 | String latitude; 18 | String longitude; 19 | String city; 20 | String state; 21 | String country; 22 | String zipCode; 23 | } 24 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | postgresql 6 | true 7 | org.postgresql.Driver 8 | jdbc:postgresql://localhost:5432/uber 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/PaymentReceipt.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.ManyToOne; 7 | import javax.persistence.OneToOne; 8 | import javax.persistence.Table; 9 | 10 | 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Entity 15 | @Table(name = "paymentreceipt") 16 | @Getter 17 | @Setter 18 | public class PaymentReceipt extends Auditable { 19 | @OneToOne 20 | private Booking booking; 21 | private Double amount; 22 | @ManyToOne 23 | private PaymentGateway paymentGateway; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/repositories/RoleRepository.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.repositories; 2 | 3 | import com.uber.api.models.Role; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.Optional; 8 | 9 | @Repository 10 | public interface RoleRepository extends JpaRepository { 11 | 12 | Optional findFirstByName(String name); 13 | 14 | // I want a function that gets me the account 15 | // that account should have a password of xyz 16 | 17 | // CRUD, find-by-attribute, find-by-attribute-of-attribute 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/repositories/AccountRepository.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.repositories; 2 | 3 | import com.uber.api.models.Account; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.Optional; 8 | 9 | @Repository 10 | public interface AccountRepository extends JpaRepository { 11 | 12 | Optional findFirstByPhoneNumber(String phoneNumber); 13 | 14 | // I want a function that gets me the account 15 | // that account should have a password of xyz 16 | 17 | // CRUD, find-by-attribute, find-by-attribute-of-attribute 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Car.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.ManyToOne; 7 | import javax.persistence.OneToOne; 8 | import javax.persistence.Table; 9 | 10 | @Builder 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Entity 14 | @Table(name = "car") 15 | @Getter 16 | @Setter 17 | public class Car extends Auditable { 18 | private String licensePlateNumber; 19 | 20 | private String model; 21 | 22 | private String brand; 23 | 24 | @OneToOne 25 | private Driver driver; 26 | 27 | @ManyToOne 28 | private Color color; 29 | 30 | private CarType carType; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/repositories/PassengerRepository.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.repositories; 2 | 3 | import com.uber.api.models.Account; 4 | import com.uber.api.models.Passenger; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.Optional; 9 | 10 | @Repository 11 | public interface PassengerRepository extends JpaRepository { 12 | Optional findFirstByAccount_PhoneNumber(String phoneNumber); 13 | // I want a function that gets me the account 14 | // that account should have a password of xyz 15 | 16 | // CRUD, find-by-attribute, find-by-attribute-of-attribute 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/repositories/DriverRepository.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.repositories; 2 | 3 | import com.uber.api.models.Driver; 4 | import com.uber.api.models.Passenger; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.Optional; 9 | 10 | @Repository 11 | public interface DriverRepository extends JpaRepository { 12 | Optional findFirstByAccount_PhoneNumber(String phoneNumber); 13 | // I want a function that gets me the account 14 | // that account should have a password of xyz 15 | 16 | // CRUD, find-by-attribute, find-by-attribute-of-attribute 17 | 18 | } 19 | 20 | // DAO - about a single object 21 | // Repo - DAL for a collection of similar objects -------------------------------------------------------------------------------- /src/main/java/com/uber/api/security/UberSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.security; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | 8 | @Configuration 9 | @EnableWebSecurity 10 | public class UberSecurityConfig extends WebSecurityConfigurerAdapter { 11 | @Override 12 | protected void configure(HttpSecurity http) throws Exception { 13 | http.authorizeRequests() 14 | .antMatchers("/").permitAll() 15 | .anyRequest().permitAll(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Account.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.ManyToMany; 8 | import javax.persistence.Table; 9 | import java.util.List; 10 | 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Entity 15 | @Table(name = "account") 16 | public class Account extends Auditable { 17 | @Column(unique = true) 18 | private String phoneNumber; 19 | 20 | @NonNull 21 | private String password; 22 | 23 | @ManyToMany 24 | @Singular 25 | private List roles; 26 | } 27 | 28 | 29 | // User class 30 | 31 | // Role Based Authentication 32 | // Role - Instructor, HR 33 | // Permissions - canAddMentor, canSendAnnouncement, canCreateClass -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Booking.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | 4 | import lombok.*; 5 | 6 | import javax.persistence.*; 7 | import java.util.List; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Entity 13 | @Table(name = "booking") 14 | @Getter 15 | @Setter 16 | public class Booking extends Auditable { 17 | @ManyToOne 18 | private Passenger passenger; 19 | 20 | @ManyToOne 21 | private Driver driver; 22 | 23 | private BookingStatus bookingStatus; 24 | 25 | private BookingType bookingType; 26 | 27 | @OneToOne 28 | private OTP rideStartOTP; 29 | 30 | @OneToOne(mappedBy = "booking", cascade = CascadeType.ALL) 31 | private PaymentReceipt paymentReceipt; 32 | 33 | @ManyToMany 34 | private List route; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:postgresql://localhost:5432/uber 2 | spring.datasource.username=uberadmin 3 | spring.datasource.password=uberadmin123 4 | 5 | # ORM - Object Relational Mapper (Spring JPA + Hibernate) 6 | # Java Classes and Objects 7 | spring.jpa.hibernate.ddl-auto=update 8 | spring.jpa.show-sql=true 9 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect 10 | 11 | 12 | # new-table with the new schema 13 | # staging-table with the new schema 14 | # old table with old schema 15 | 16 | # read things from old table 17 | # and put them in staging table 18 | 19 | # staging data will be moved to new-table 20 | # drop the old table, and start using the new-table 21 | 22 | # small project: framework - provide migration script 23 | # large scale org: DB team -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Passenger.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | import lombok.*; 4 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 5 | 6 | import javax.persistence.*; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | // constructor private 11 | // java reflection API 12 | // expose a public constructor 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Entity 18 | @Table(name="passenger") 19 | @Getter @Setter 20 | public class Passenger extends Auditable { 21 | private String name; 22 | 23 | private Gender gender; 24 | 25 | @ManyToOne 26 | private Location location; 27 | 28 | @OneToMany(mappedBy = "passenger") 29 | private List bookings = new ArrayList<>(); 30 | 31 | @OneToOne(cascade = CascadeType.ALL) 32 | private Account account; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/OTP.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | 4 | import lombok.*; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.Table; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Entity 13 | @Table(name = "otp") 14 | @Getter 15 | @Setter 16 | public class OTP extends Auditable { 17 | private String code; 18 | 19 | private String sentToNumber; 20 | 21 | @Builder.Default 22 | private Integer attemptsMade = 0; 23 | 24 | @Builder.Default 25 | private Boolean isValid = true; 26 | 27 | public static OTP randomOTP(String sentToNumber) { 28 | return OTP.builder() 29 | .code("0000") 30 | .sentToNumber(sentToNumber) 31 | .build(); 32 | } 33 | } 34 | 35 | // 1. Disk is cheap 36 | // 2. Premature optimization is the root of all evil 37 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/controllers/DriverController.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.controllers; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RequestMapping("/driver") 9 | @RestController 10 | public class DriverController { 11 | /* 12 | 13 | REST APIs for Driver 14 | -------------------- 15 | 16 | GET /bookings - get all the bookings for this driver 17 | GET/booking/id - similar 18 | PUT /booking/id/arrived - mark the booking-status as cab-arrived 19 | PUT /booking/id/start-ride - mark the booking-status as started, verify the otp 20 | PUT /booking/id/end-ride 21 | PUT /update-location 22 | PUT /booking/id/cancel - cancel the booking 23 | PUT /booking/id/accept - accept the booking that doesnt currently belong to this driver 24 | 25 | // make sure that this driver is compatible with this bookings 26 | 27 | 28 | 29 | 30 | PUT /turn-off-availability - turn off the driver availability 31 | PUT /turn-on-availability - turn off the driver availability 32 | PUT /update-status 33 | PUT /toggle-availability // No, because not idempotent 34 | */ 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Driver.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.util.List; 7 | 8 | @Builder 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Entity 12 | @Table(name = "driver") 13 | @Getter 14 | @Setter 15 | public class Driver extends Auditable { 16 | private Gender gender; 17 | 18 | @OneToOne 19 | private Car car; 20 | 21 | @OneToMany(mappedBy = "driver") 22 | private List bookings; 23 | 24 | @OneToOne 25 | private Booking currentlyActiveBooking; 26 | 27 | @Builder.Default 28 | private Boolean isAvailable = true; 29 | 30 | @ManyToOne 31 | private Location location; 32 | 33 | private String name; 34 | 35 | @OneToOne(cascade = CascadeType.ALL) 36 | private Account account; 37 | } 38 | 39 | // inheritance is an OOP concept - objects and relations 40 | // DBs don't have inheritance - tables/rows and relations 41 | 42 | /* 43 | 44 | User: 45 | id 46 | password 47 | 48 | Passenger(User): 49 | name 50 | 51 | Driver(User): 52 | age 53 | 54 | # Per table inheritance 55 | Passenger 56 | id password name 57 | 58 | Driver 59 | id password name 60 | 61 | User 62 | id password 63 | 64 | Driver 65 | name user_id 66 | 67 | Passenger 68 | name user_id 69 | */ -------------------------------------------------------------------------------- /src/main/java/com/uber/api/models/Auditable.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.models; 2 | 3 | 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.springframework.data.annotation.CreatedDate; 7 | import org.springframework.data.annotation.LastModifiedDate; 8 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 9 | 10 | import javax.persistence.*; 11 | import java.io.Serializable; 12 | import java.util.Date; 13 | 14 | @Getter 15 | @Setter 16 | @EntityListeners(AuditingEntityListener.class) 17 | @MappedSuperclass 18 | public abstract class Auditable implements Serializable { 19 | @Id 20 | @GeneratedValue 21 | @SequenceGenerator(name = "idsequence", allocationSize = 10) 22 | private Long id; 23 | // 24 | // @Column(nullable = false, updatable = false) 25 | @CreatedDate 26 | @Temporal(TemporalType.TIMESTAMP) 27 | private Date createdAt = new Date(); 28 | 29 | // @Column(nullable = false, updatable = true) 30 | @LastModifiedDate 31 | @Temporal(TemporalType.TIMESTAMP) 32 | private Date updatedAt = new Date(); 33 | 34 | @Override 35 | public int hashCode() { 36 | return id.hashCode(); 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) { 41 | if (obj instanceof Auditable) { 42 | return ((Auditable) obj).getId().equals(getId()); 43 | } 44 | return super.equals(obj); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/services/BookingService.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.services; 2 | 3 | import com.uber.api.models.Booking; 4 | import com.uber.api.models.Driver; 5 | import com.uber.api.repositories.BookingRepository; 6 | import com.uber.api.repositories.DriverRepository; 7 | 8 | public class BookingService { 9 | BookingRepository bookingRepository; 10 | DriverRepository driverRepository; 11 | 12 | public void requestBooking(Booking booking) { 13 | // find the start location of the booking 14 | // search drivers near that location 15 | // send push notifications to those drivers 16 | } 17 | 18 | public synchronized boolean acceptBooking(Booking booking, Driver driver) { 19 | // incomplete, because 20 | // this enforces only 1 driver can be accepting a booking 21 | // 1 driver can accept 1 booking 22 | // multiple drivers can book different bookings at the same time 23 | if(driver.getCurrentlyActiveBooking() != null) { 24 | return false; 25 | } 26 | if(!driver.getIsAvailable()) { 27 | return false; 28 | } 29 | // calculate the distance b/w the driver and the passenger 30 | // make sure that it is within a limit 31 | // estimate ETA from eta service, and ensure that is is within limit 32 | driver.setCurrentlyActiveBooking(booking); 33 | booking.setDriver(driver); 34 | bookingRepository.save(booking); 35 | driverRepository.save(driver); 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Notes: https://drive.google.com/file/d/1WsrfjzPwtE1d-z94SeVRdOscReeliPQN/view?usp=sharing 2 | 3 | Project Setup 4 | ------------- 5 | 6 | - Create project here [Spring Boot Starter](https://start.spring.io) 7 | - Dependencies 8 | + all dev (lombok, dev, config) 9 | + web mvc 10 | + data-rest 11 | + jpa (lombok) 12 | + postgresql driver 13 | + spring security 14 | + spring sessions 15 | - download the zip, open in your editor 16 | - setup DB config in `uber/src/main/resources/application.properties` 17 | 18 | DB Setup for Local Dev 19 | ---------------------- 20 | 21 | Enter the postgres shell: `sudo -u postgres psql` 22 | List users (in psql) `\l` 23 | List databases (in psql) `\du` 24 | Quit the psql shell `\q` 25 | 26 | Create a new DB (in psql) `CREATE DATABASE uber;` 27 | Delete a new DB (in psql) `DROP DATABASE uber;` 28 | 29 | Create a new user with password and db create-write and superuser permissions (shell): `sudo -u postgres createuser -d -l -s -P uberadmin` 30 | Delete user (shell) `sudo -u postgres dropuser username` 31 | 32 | Other Notes 33 | ----------- 34 | 35 | ```py 36 | class Driver: 37 | @OneToOne 38 | account: Account # in the driver table, create a col account_id 39 | 40 | @OneToMany 41 | bookings: [Booking] # bookings table, driver_id 42 | # Rohan.bookings() 43 | # GET * from Booking 44 | # where user_id = RohanId 45 | 46 | class Passenger: 47 | account: Account 48 | bookings: [Booking] 49 | 50 | class Account: 51 | # can be for a driver 52 | # can be for a passenger 53 | 54 | class Booking: 55 | @ManyToOne 56 | driver: Driver 57 | passenger: Passenger 58 | ``` 59 | 60 | Driver 1 - * Booking 61 | Passenger 1 - * Booking 62 | Driver 1 - 1 Account 63 | Passenger 1 - 1 Account 64 | 65 | 66 | 67 | Python - SQLAlchemy ORM - forces you to define relations both ways 68 | Django ORM - allows you to define one way relations 69 | 70 | 71 | ORM is just an abstractions 72 | DBs are truth tables with constraints 73 | 74 | every row in a table denotes a truth 75 | -------------------------------------------------------------------------------- /HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/maven-plugin/reference/html/#build-image) 9 | * [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#using-boot-devtools) 10 | * [Spring Configuration Processor](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#configuration-metadata-annotation-processor) 11 | * [Spring Web](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications) 12 | * [Rest Repositories](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#howto-use-exposing-spring-data-repositories-rest-endpoint) 13 | * [Spring Security](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#boot-features-security) 14 | * [Spring Data JPA](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#boot-features-jpa-and-spring-data) 15 | 16 | ### Guides 17 | The following guides illustrate how to use some features concretely: 18 | 19 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 20 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 21 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) 22 | * [Accessing JPA Data with REST](https://spring.io/guides/gs/accessing-data-rest/) 23 | * [Accessing Neo4j Data with REST](https://spring.io/guides/gs/accessing-neo4j-data-rest/) 24 | * [Accessing MongoDB Data with REST](https://spring.io/guides/gs/accessing-mongodb-data-rest/) 25 | * [Securing a Web Application](https://spring.io/guides/gs/securing-web/) 26 | * [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) 27 | * [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) 28 | * [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/services/TrackingService.java: -------------------------------------------------------------------------------- 1 | //package com.uber.api.services; 2 | // 3 | //import com.uber.api.models.Driver; 4 | //import com.uber.api.models.Location; 5 | //import com.uber.api.repositories.DriverRepository; 6 | //import com.uber.api.utils.QuadTree; 7 | //import lombok.AllArgsConstructor; 8 | // 9 | //import java.util.*; 10 | //import java.util.stream.Collectors; 11 | // 12 | //// Quad-Tree 13 | //// splits your 2-d plane into quadrants 14 | //// 15 | ////class QTNode { 16 | //// long key; 17 | //// int x, y; 18 | //// long count; 19 | //// QTNode ne = null, nw = null, se= null, sw = null; 20 | //// 21 | //// public QTNode(Point topleft, Point bottomRight) { 22 | //// this.key = key; 23 | //// count = 0; 24 | //// } 25 | //// 26 | //// public void add(int x, int y) { 27 | //// if(count == 0) { 28 | //// this.x = x; 29 | //// this.y = y; 30 | //// } 31 | //// else if(count == 1) { 32 | //// ne = new QTNode() 33 | //// } 34 | //// } 35 | ////} 36 | //// 37 | ////class com.uber.api.utils.QuadTree { 38 | //// 39 | ////} 40 | // 41 | //@AllArgsConstructor 42 | //class Point { 43 | // long x, y; 44 | // public static Point fromLocation(Location location) { 45 | // return new Point(0, 0); 46 | // } 47 | //} 48 | // 49 | //public class TrackingService { 50 | // DriverRepository driverRepository; 51 | // 52 | // QuadTree quadTree = new QuadTree(new QuadTree.Rect(0, 0, 100, 100)); 53 | // Map> driversForLocation; 54 | // 55 | // public void updateDriverLocation(Driver driver, Location location) { 56 | // // remove the previous location from the quadTree 57 | // // add the current position 58 | // Point p = Point.fromLocation(location); 59 | // quadTree.add(p.x, p.y); 60 | // driversForLocation.getOrDefault(p, new HashSet<>()).add(driver.getId()); 61 | // } 62 | // 63 | // public List getNearbyDrivers(Location location) { 64 | // Point p = Point.fromLocation(location); 65 | // List driverIds = new ArrayList<>(); 66 | // for(QuadTree.Pt point: quadTree.kNearestNeighbors(10, p.x, p.y)) { 67 | // driverIds.addAll(driversForLocation.get(new Point(point.x, point.y))) 68 | // } 69 | // return driverIds 70 | // .stream() 71 | // .map(x -> driverRepository.findById(x).get()) 72 | // .collect(Collectors.toList()); 73 | // } 74 | //} 75 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/controllers/PassengerController.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.controllers; 2 | 3 | import com.uber.api.models.Booking; 4 | import com.uber.api.models.BookingStatus; 5 | import com.uber.api.models.Passenger; 6 | import com.uber.api.repositories.AccountRepository; 7 | import com.uber.api.repositories.BookingRepository; 8 | import com.uber.api.repositories.PassengerRepository; 9 | import com.uber.api.services.BookingService; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | @RestController 14 | @RequestMapping("/passenger") 15 | public class PassengerController { 16 | AccountRepository accountRepository; 17 | BookingRepository bookingRepository; 18 | PassengerRepository passengerRepository; 19 | BookingService bookingService; 20 | BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); 21 | 22 | private Passenger fromPhone(String phoneNumeber) { 23 | return passengerRepository.findFirstByAccount_PhoneNumber(phoneNumeber).orElseThrow(); 24 | } 25 | 26 | @PostMapping("/{phoneNumber}/bookings") 27 | public void requestBooking(@PathVariable(name="phoneNumber") String phoneNumber, 28 | @RequestBody Booking data) { 29 | Passenger passenger = fromPhone(phoneNumber); 30 | 31 | // exception 32 | // your exception should be as specific as possible 33 | // throw new Exception(); // puppies die 34 | // this passenger is authenticated 35 | 36 | Booking booking = Booking.builder() 37 | .passenger(passenger) 38 | .bookingStatus(BookingStatus.REQUESTED) 39 | .bookingType(data.getBookingType()) 40 | .build(); 41 | bookingRepository.save(booking); 42 | bookingService.requestBooking(booking); 43 | } 44 | 45 | 46 | /* 47 | REST APIs to create 48 | ------------------- 49 | 50 | min(y, min(x, min(x, y))) 51 | x + y != x + x + y 52 | 53 | Safe (Read only) & Idempotent (multiple applications dont change) - GET 54 | Idempotent - PUT, PATCH 55 | POST - neither safe, nor idempotent 56 | 57 | GET /bookings 58 | - return all bookings of this passenger 59 | 60 | GET /booking/id 61 | - make sure that that booking actually belongs to this passenger 62 | - return the details of the booking 63 | 64 | PUT /booking/id 65 | - make sure that that booking actually belongs to this passenger 66 | - update the booking 67 | - return 200 status 68 | 69 | POST /bookings 70 | - Request a booking for the user 71 | 72 | PUT /booking/id/cancel 73 | - make sure that that booking actually belongs to this passenger 74 | - cancel the booking 75 | 76 | PUT / 77 | - update the user details 78 | 79 | 80 | Banking application 81 | /send-money?amount=1000 # Disaster 82 | PUT /transactionRequest - unique code - amount-to-transfer 83 | 84 | 85 | Swiggy - place the same order twice, an automatic call comes to you 86 | 87 | 88 | */ 89 | 90 | 91 | } 92 | // /passenger 93 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.4.RELEASE 9 | 10 | 11 | com.uber 12 | api 13 | 0.0.1-SNAPSHOT 14 | UberAPI 15 | Backend APIs for Uber 16 | 17 | 18 | 11 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-data-rest 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-security 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 38 | 39 | org.springframework.session 40 | spring-session-core 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-devtools 46 | runtime 47 | true 48 | 49 | 50 | org.postgresql 51 | postgresql 52 | runtime 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-configuration-processor 57 | true 58 | 59 | 60 | org.projectlombok 61 | lombok 62 | true 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-test 67 | test 68 | 69 | 70 | org.junit.vintage 71 | junit-vintage-engine 72 | 73 | 74 | 75 | 76 | org.springframework.security 77 | spring-security-test 78 | test 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/controllers/DBInitController.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.controllers; 2 | 3 | import com.uber.api.models.Account; 4 | import com.uber.api.models.Gender; 5 | import com.uber.api.models.Passenger; 6 | import com.uber.api.models.Role; 7 | import com.uber.api.repositories.AccountRepository; 8 | import com.uber.api.repositories.PassengerRepository; 9 | import com.uber.api.repositories.RoleRepository; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | // @Component - tag this on any class that Spring should manage 16 | // @Entity - Spring JPA 17 | // @Repository - @Component + exception handling 18 | // @Controller - @Component + dealing with HTTP requests and Responses + exception handling 19 | // @Service - @Component + some HTTP stuff - deals with the service layer, should not directly interact with DB 20 | // @RestController - @Controller + Automatic Serialization 21 | 22 | // threadlocal will be created 23 | // singleton 24 | 25 | @RestController 26 | public class DBInitController { 27 | // spring automatically creates an implementation for me 28 | // and also sets that as the default implementation 29 | // Inversion of Control 30 | @Autowired 31 | AccountRepository accountRepository; 32 | @Autowired 33 | PassengerRepository passengerRepository; 34 | @Autowired 35 | RoleRepository roleRepository; 36 | BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); 37 | 38 | 39 | @GetMapping("/db-init") 40 | public Passenger hello() { 41 | accountRepository.deleteAll(); 42 | 43 | Role adminRole = Role.builder() 44 | .name("ADMIN") 45 | .description("All access") 46 | .build(); 47 | Role passengerRole = Role.builder() 48 | .name("PASSENGER") 49 | .description("All access") 50 | .build(); 51 | Role driverRole = Role.builder() 52 | .name("DRIVER") 53 | .description("All access") 54 | .build(); 55 | roleRepository.save(adminRole); 56 | roleRepository.save(passengerRole); 57 | roleRepository.save(driverRole); 58 | 59 | Account account = Account.builder() 60 | .phoneNumber("000") 61 | .password(bCryptPasswordEncoder.encode("meat")) 62 | .role(passengerRole) 63 | .build(); 64 | accountRepository.save(account); 65 | Passenger luffy = Passenger.builder() 66 | .gender(Gender.MALE) 67 | .name("Luffy") 68 | .account(account) 69 | .build(); 70 | passengerRepository.save(luffy); 71 | return luffy; 72 | } 73 | } 74 | 75 | // Roles - Driver, Passenger, Admin 76 | // Colors 77 | 78 | // Dependency Injection and IoC 79 | // hashing 80 | // hash + salt 81 | 82 | 83 | // MD5 - crack this on your laptop / phone - extremely weak 84 | // SHA256 - still not recommended 85 | // BCrypt - current standard 86 | // Scrypt 87 | // .... - will be the standard in the next 10 years -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /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 | # https://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 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/java/com/uber/api/utils/QuadTree.java: -------------------------------------------------------------------------------- 1 | package com.uber.api.utils; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import static java.lang.Double.POSITIVE_INFINITY; 7 | 8 | import java.util.*; 9 | 10 | public class QuadTree { 11 | 12 | private static int NORTH_EAST = 1; 13 | private static int NORTH_WEST = 2; 14 | private static int SOUTH_EAST = 3; 15 | private static int SOUTH_WEST = 4; 16 | 17 | private static boolean isNorth(int dir) { 18 | return dir == NORTH_EAST || dir == NORTH_WEST; 19 | } 20 | 21 | public static class Pt { 22 | public long x, y; 23 | 24 | public Pt(long xx, long yy) { 25 | y = yy; 26 | x = xx; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "(" + x + "," + y + ")"; 32 | } 33 | } 34 | 35 | static class SortedPt implements Comparable { 36 | Pt pt; 37 | double dist; 38 | 39 | public SortedPt(double dist, Pt pt) { 40 | this.dist = dist; 41 | this.pt = pt; 42 | } 43 | 44 | @Override 45 | public int compareTo(SortedPt other) { 46 | return Double.compare(dist, other.dist); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return dist + " - " + pt; 52 | } 53 | } 54 | 55 | // Node that represents a regions with points inside this region. 56 | class Node { 57 | 58 | // Keeps track of how many points are currently 59 | // contained within this quad tree node. 60 | private int ptCount = 0; 61 | 62 | // Tracks the (x,y) coordinates of points within this quad tree node. 63 | private long[] X, Y; 64 | 65 | // Define four Quad Tree nodes to subdivide the region we're 66 | // considering into four parts: north west (nw), north east (ne), 67 | // south west(sw) and south east(se). 68 | private Node nw, ne, sw, se; 69 | 70 | // The region this node encompasses 71 | private Rect region; 72 | 73 | // Construct a quad tree for a particular region. 74 | public Node(Rect region) { 75 | if (region == null) throw new IllegalArgumentException("Illegal argument"); 76 | this.region = region; 77 | X = new long[NUM_POINTS]; 78 | Y = new long[NUM_POINTS]; 79 | } 80 | 81 | // Try adding a point to the current region and if the 82 | // region is already full subdivide and recurse until 83 | // you are able to place the point inside a smaller region 84 | private boolean add(long x, long y) { 85 | 86 | // Point is not within this region. 87 | if (!region.contains(x, y)) return false; 88 | 89 | // The point is within this region and there is room for it. 90 | if (ptCount < NUM_POINTS) { 91 | 92 | X[ptCount] = x; 93 | Y[ptCount] = y; 94 | ptCount++; 95 | 96 | return true; 97 | 98 | // This region is full, so subdivide the region into four 99 | // quadrants and try adding the point to these new regions 100 | } else { 101 | 102 | // Find the center of this region at (cx, cy) 103 | long cx = (region.x1 + region.x2) / 2; 104 | long cy = (region.y1 + region.y2) / 2; 105 | 106 | // Lazily subdivide each of the regions into four parts 107 | // one by one as needed to save memory. 108 | 109 | if (sw == null) sw = new Node(new Rect(region.x1, region.y1, cx, cy)); 110 | if (sw.add(x, y)) return true; 111 | 112 | if (nw == null) nw = new Node(new Rect(region.x1, cy, cx, region.y2)); 113 | if (nw.add(x, y)) return true; 114 | 115 | if (ne == null) ne = new Node(new Rect(cx, cy, region.x2, region.y2)); 116 | if (ne.add(x, y)) return true; 117 | 118 | if (se == null) se = new Node(new Rect(cx, region.y1, region.x2, cy)); 119 | return se.add(x, y); 120 | } 121 | } 122 | 123 | // Count how many points are found within a certain rectangular region 124 | private int count(Rect area) { 125 | 126 | if (area == null || !region.intersects(area)) return 0; 127 | 128 | int count = 0; 129 | 130 | // The area we're considering fully contains 131 | // the region of this node, so simply add the 132 | // number of points within this region to the count 133 | if (area.contains(region)) { 134 | count = ptCount; 135 | 136 | // Our regions overlap, so some points in this 137 | // region may intersect with the area we're considering 138 | } else { 139 | for (int i = 0; i < ptCount; i++) if (area.contains(X[i], Y[i])) count++; 140 | } 141 | 142 | // Dig into each of the quadrants and count all points 143 | // which overlap with the area and sum their count 144 | if (nw != null) count += nw.count(area); 145 | if (ne != null) count += ne.count(area); 146 | if (sw != null) count += sw.count(area); 147 | if (se != null) count += se.count(area); 148 | 149 | return count; 150 | } 151 | 152 | private List kNearestNeighbors(int k, long x, long y) { 153 | PriorityQueue heap = new PriorityQueue<>(k, Collections.reverseOrder()); 154 | knn(k, x, y, heap); 155 | 156 | List neighbors = new ArrayList<>(); 157 | for (SortedPt n : heap) neighbors.add(n.pt); 158 | return neighbors; 159 | } 160 | 161 | // Find the k-nearest neighbors. 162 | private void knn(int k, long x, long y, PriorityQueue heap) { 163 | 164 | for (int i = 0; i < ptCount; i++) { 165 | long xx = X[i], yy = Y[i]; 166 | 167 | // Get largest radius. 168 | double radius = heap.isEmpty() ? POSITIVE_INFINITY : heap.peek().dist; 169 | 170 | // Get distance from point to this point. 171 | double distance = Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y)); 172 | 173 | // Add node to heap. 174 | if (heap.size() < k) { 175 | heap.add(new SortedPt(distance, new Pt(xx, yy))); 176 | } else if (distance < radius) { 177 | heap.poll(); 178 | // System.out.println("POLLED: " + heap.poll()); 179 | heap.add(new SortedPt(distance, new Pt(xx, yy))); 180 | } 181 | } 182 | 183 | int pointQuadrant = 0; 184 | 185 | // Dig to find the quadrant (x, y) belongs to. 186 | if (nw != null && region.contains(x, y)) { 187 | nw.knn(k, x, y, heap); 188 | pointQuadrant = NORTH_WEST; 189 | } else if (ne != null && region.contains(x, y)) { 190 | ne.knn(k, x, y, heap); 191 | pointQuadrant = NORTH_EAST; 192 | } else if (sw != null && region.contains(x, y)) { 193 | sw.knn(k, x, y, heap); 194 | pointQuadrant = SOUTH_WEST; 195 | } else if (se != null && region.contains(x, y)) { // Use else clause? 196 | se.knn(k, x, y, heap); 197 | pointQuadrant = SOUTH_EAST; 198 | } 199 | 200 | if (pointQuadrant == 0) { 201 | // System.out.println("UNDEFINED QUADRANT?"); 202 | // return; 203 | } 204 | 205 | // Get largest radius. 206 | double radius = heap.isEmpty() ? POSITIVE_INFINITY : heap.peek().dist; 207 | 208 | // Find the center of this region at (cx, cy) 209 | long cx = (region.x1 + region.x2) / 2; 210 | long cy = (region.y1 + region.y2) / 2; 211 | 212 | // Compute the horizontal (dx) and vertical (dy) distance from the 213 | // point (x, y) to the nearest cell. 214 | long dx = Math.abs(x - cx); 215 | long dy = Math.abs(y - cy); 216 | 217 | boolean checkHorizontalCell = radius >= dx; 218 | boolean checkVerticalCell = radius >= dy; 219 | boolean checkDiagonalCell = checkHorizontalCell && checkVerticalCell; 220 | 221 | // TODO(williamfiset): Refactor. 222 | if (heap.size() == k) { 223 | 224 | if (isNorth(pointQuadrant)) { 225 | if (pointQuadrant == NORTH_WEST) { 226 | if (checkHorizontalCell) if (ne != null) ne.knn(k, x, y, heap); 227 | if (checkVerticalCell) if (sw != null) sw.knn(k, x, y, heap); 228 | if (checkDiagonalCell) if (se != null) se.knn(k, x, y, heap); 229 | } else { 230 | if (checkHorizontalCell) if (nw != null) nw.knn(k, x, y, heap); 231 | if (checkVerticalCell) if (se != null) se.knn(k, x, y, heap); 232 | if (checkDiagonalCell) if (nw != null) nw.knn(k, x, y, heap); 233 | } 234 | } else { 235 | if (pointQuadrant == SOUTH_WEST) { 236 | if (checkHorizontalCell) if (se != null) se.knn(k, x, y, heap); 237 | if (checkVerticalCell) if (nw != null) nw.knn(k, x, y, heap); 238 | if (checkDiagonalCell) if (ne != null) ne.knn(k, x, y, heap); 239 | } else { 240 | if (checkHorizontalCell) if (sw != null) sw.knn(k, x, y, heap); 241 | if (checkVerticalCell) if (ne != null) ne.knn(k, x, y, heap); 242 | if (checkDiagonalCell) if (nw != null) nw.knn(k, x, y, heap); 243 | } 244 | } 245 | 246 | // Still need to find k - heap.size() nodes! 247 | } else { 248 | 249 | // explore all quadrants ? 250 | // Do it lazy? Inspect return val after each call? 251 | 252 | for (int quadrant = 1; quadrant <= 4; quadrant++) { 253 | 254 | if (quadrant == pointQuadrant) continue; 255 | radius = heap.isEmpty() ? POSITIVE_INFINITY : heap.peek().dist; 256 | checkHorizontalCell = radius >= dx; 257 | checkVerticalCell = radius >= dy; 258 | checkDiagonalCell = checkHorizontalCell && checkVerticalCell; 259 | 260 | // No validation 261 | if (heap.size() != k) { 262 | if (isNorth(pointQuadrant)) { 263 | if (pointQuadrant == NORTH_WEST) { 264 | if (ne != null) ne.knn(k, x, y, heap); 265 | if (sw != null) sw.knn(k, x, y, heap); 266 | if (se != null) se.knn(k, x, y, heap); 267 | } else { 268 | if (nw != null) nw.knn(k, x, y, heap); 269 | if (se != null) se.knn(k, x, y, heap); 270 | if (nw != null) nw.knn(k, x, y, heap); 271 | } 272 | } else { 273 | if (pointQuadrant == SOUTH_WEST) { 274 | if (se != null) se.knn(k, x, y, heap); 275 | if (nw != null) nw.knn(k, x, y, heap); 276 | if (ne != null) ne.knn(k, x, y, heap); 277 | } else { 278 | if (sw != null) sw.knn(k, x, y, heap); 279 | if (ne != null) ne.knn(k, x, y, heap); 280 | if (nw != null) nw.knn(k, x, y, heap); 281 | } 282 | } 283 | 284 | // must intersect 285 | } else { 286 | 287 | if (isNorth(pointQuadrant)) { 288 | if (pointQuadrant == NORTH_WEST) { 289 | if (checkHorizontalCell) if (ne != null) ne.knn(k, x, y, heap); 290 | if (checkVerticalCell) if (sw != null) sw.knn(k, x, y, heap); 291 | if (checkDiagonalCell) if (se != null) se.knn(k, x, y, heap); 292 | } else { 293 | if (checkHorizontalCell) if (nw != null) nw.knn(k, x, y, heap); 294 | if (checkVerticalCell) if (se != null) se.knn(k, x, y, heap); 295 | if (checkDiagonalCell) if (nw != null) nw.knn(k, x, y, heap); 296 | } 297 | } else { 298 | if (pointQuadrant == SOUTH_WEST) { 299 | if (checkHorizontalCell) if (se != null) se.knn(k, x, y, heap); 300 | if (checkVerticalCell) if (nw != null) nw.knn(k, x, y, heap); 301 | if (checkDiagonalCell) if (ne != null) ne.knn(k, x, y, heap); 302 | } else { 303 | if (checkHorizontalCell) if (sw != null) sw.knn(k, x, y, heap); 304 | if (checkVerticalCell) if (ne != null) ne.knn(k, x, y, heap); 305 | if (checkDiagonalCell) if (nw != null) nw.knn(k, x, y, heap); 306 | } 307 | } 308 | } 309 | } // for 310 | } // if 311 | } // method 312 | } // node 313 | 314 | public static class Rect { 315 | 316 | long x1, y1, x2, y2; 317 | 318 | // Define a rectangle as a pair of points (x1, y1) in the bottom left corner 319 | // and (x2, y2) in the top right corner of the rectangle. 320 | public Rect(long x1, long y1, long x2, long y2) { 321 | if (x1 > x2 || y1 > y2) throw new IllegalArgumentException("Illegal rectangle coordinates"); 322 | this.x1 = x1; 323 | this.y1 = y1; 324 | this.x2 = x2; 325 | this.y2 = y2; 326 | } 327 | 328 | // Check for an intersection between two rectangles. The easiest way to do this is to 329 | // check if the two rectangles do not intersect and negate the logic afterwards. 330 | public boolean intersects(Rect r) { 331 | return r != null && !(r.x2 < x1 || r.x1 > x2 || r.y1 > y2 || r.y2 < y1); 332 | } 333 | 334 | // Check if a point (x, y) is within this rectangle, this 335 | // includes the boundary of the rectangle. 336 | public boolean contains(long x, long y) { 337 | return (x1 <= x && x <= x2) && (y1 <= y && y <= y2); 338 | } 339 | 340 | // Check if another rectangle is strictly contained within this rectangle. 341 | public boolean contains(Rect r) { 342 | return r != null && contains(r.x1, r.y1) && contains(r.x2, r.y2); 343 | } 344 | } 345 | 346 | // This is the maximum number of points each quad tree node can 347 | // sustain before it has to subdivide into four more regions. 348 | // This variable can have a significant impact on performance. 349 | final int NUM_POINTS; 350 | 351 | public static final int DEFAULT_NUM_POINTS = 16; 352 | 353 | // Root node of the quad tree. Public for testing. 354 | public Node root; 355 | 356 | public QuadTree(Rect region) { 357 | this.NUM_POINTS = DEFAULT_NUM_POINTS; 358 | root = new Node(region); 359 | } 360 | 361 | public QuadTree(Rect region, int pointsPerNode) { 362 | this.NUM_POINTS = pointsPerNode; 363 | root = new Node(region); 364 | } 365 | 366 | public boolean add(long x, long y) { 367 | return root.add(x, y); 368 | } 369 | 370 | public int count(Rect region) { 371 | return root.count(region); 372 | } 373 | 374 | public List kNearestNeighbors(int k, long x, long y) { 375 | return root.kNearestNeighbors(k, x, y); 376 | } 377 | 378 | public List getPoints() { 379 | List points = new ArrayList<>(); 380 | getPoints(root, points); 381 | return points; 382 | } 383 | 384 | private void getPoints(Node node, List points) { 385 | if (node == null) return; 386 | for (int i = 0; i < node.ptCount; i++) points.add(new Pt(node.X[i], node.Y[i])); 387 | getPoints(node.nw, points); 388 | getPoints(node.ne, points); 389 | getPoints(node.sw, points); 390 | getPoints(node.se, points); 391 | } 392 | } --------------------------------------------------------------------------------