├── .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 |
18 |
19 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
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 | }
--------------------------------------------------------------------------------