-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path45.DesignAGeo-LocationService.java
More file actions
159 lines (129 loc) · 5.06 KB
/
45.DesignAGeo-LocationService.java
File metadata and controls
159 lines (129 loc) · 5.06 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* ---------------- GEO LOCATION SERVICE DEMO (Java, Single File) ---------------- */
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/* -------- Distance Strategy Interface -------- */
interface DistanceStrategy {
// Line: Defines a method to compute distance between two lat/lon points.
double calculate(double lat1, double lon1, double lat2, double lon2);
}
/* -------- Haversine Strategy Implementation -------- */
class HaversineDistance implements DistanceStrategy {
// Line: Implements accurate spherical distance.
public double calculate(double lat1, double lon1, double lat2, double lon2) {
double R = 6371; // Earth radius in km
// Line: Convert deltas to radians.
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
// Line: Apply Haversine formula.
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
* Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // Line: Final distance in km.
}
}
/* -------- Location Model -------- */
class Location {
// Line: Each location object stores name & coordinates.
String name;
double lat;
double lon;
Location(String name, double lat, double lon) {
// Line: Assign fields.
this.name = name;
this.lat = lat;
this.lon = lon;
}
}
/* -------- Singleton GeoService -------- */
class GeoService {
// Line: SINGLETON instance storage.
private static GeoService instance;
// Line: In-memory location storage.
private final Map<Integer, Location> locations = new HashMap<>();
// Line: Global ID counter for locations.
private int idCounter = 1;
// Line: Lock for thread-safe writes.
private final ReentrantLock lock = new ReentrantLock();
// Line: Pluggable distance strategy.
private DistanceStrategy distanceStrategy = new HaversineDistance();
// Line: Private constructor ensures Singleton pattern.
private GeoService() {}
// Line: Static method to get ONE instance only.
public static GeoService getInstance() {
if (instance == null) {
synchronized (GeoService.class) { // Line: Double-checked locking.
if (instance == null) instance = new GeoService();
}
}
return instance;
}
/* -------- Add Location -------- */
public int addLocation(String name, double lat, double lon) {
lock.lock(); // Line: Ensure thread-safe write.
try {
int id = idCounter++;
locations.put(id, new Location(name, lat, lon)); // Line: Store new entry.
return id;
} finally {
lock.unlock();
}
}
/* -------- Update Location -------- */
public void updateLocation(int id, String name, Double lat, Double lon) {
lock.lock(); // Line: Protect modification.
try {
if (locations.containsKey(id)) {
Location loc = locations.get(id);
if (name != null) loc.name = name;
if (lat != null) loc.lat = lat;
if (lon != null) loc.lon = lon;
}
} finally {
lock.unlock();
}
}
/* -------- Delete Location -------- */
public void deleteLocation(int id) {
lock.lock();
try {
locations.remove(id); // Line: Remove entry if present.
} finally {
lock.unlock();
}
}
/* -------- Get Nearby -------- */
public List<String> getNearby(double lat, double lon, double radiusKm) {
// Line: Results stored as sorted list of: "name (distance km)".
List<Map.Entry<Double, String>> res = new ArrayList<>();
// Line: Linear scan across stored locations.
for (Location loc : locations.values()) {
double dist = distanceStrategy.calculate(lat, lon, loc.lat, loc.lon);
if (dist <= radiusKm) {
res.add(Map.entry(dist, loc.name));
}
}
// Line: Sort by distance.
res.sort(Map.Entry.comparingByKey());
// Line: Format output list.
List<String> finalList = new ArrayList<>();
for (var e : res) {
finalList.add(e.getValue() + " (" + e.getKey() + " km)");
}
return finalList;
}
}
/* -------- DEMO MAIN (Runs in VS Code) -------- */
public class GeoLocationDemo {
public static void main(String[] args) {
GeoService geo = GeoService.getInstance(); // Line: Get singleton instance.
// Line: Add two example locations.
int id1 = geo.addLocation("Restaurant A", 28.6139, 77.2090);
int id2 = geo.addLocation("Cafe B", 28.7041, 77.1025);
// Line: Query within 15 km radius.
List<String> nearby = geo.getNearby(28.61, 77.20, 15);
// Line: Print results.
System.out.println("Nearby locations:");
for (String s : nearby) System.out.println(s);
}
}