DebasishDhal99 commited on
Commit
d6ae08e
·
1 Parent(s): 1bfa936

Add basic nearby wikisearch, both backend and frontend

Browse files

- Used wikipedia api to look for nearby wiki pages.
- Get coordinates from backend and render markers over them
- Search radius range = [10, 10,000] meters, from server side
- I had to deal with a very strange case, where GET request didn't work at all, no matter what. Rebuilt the docker image,

Files changed (2) hide show
  1. frontend/src/components/Map.js +222 -3
  2. main.py +64 -1
frontend/src/components/Map.js CHANGED
@@ -82,6 +82,12 @@ const Map = ( { onMapClick, searchQuery, contentType, setSearchQuery, setSubmitt
82
 
83
  const [countryBorders, setCountryBorders] = useState(null);
84
 
 
 
 
 
 
 
85
  const CenterMap = ({ position }) => {
86
  const map = useMap();
87
  useEffect(() => {
@@ -208,7 +214,36 @@ const Map = ( { onMapClick, searchQuery, contentType, setSearchQuery, setSubmitt
208
  };
209
 
210
  const handleMapClick = useCallback(async (lat, lon) => {
211
- if (geoToolMode === "distance") {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  const updatedPoints = [...geoPoints, { lat, lon }];
213
  if (updatedPoints.length > 2) {
214
  updatedPoints.shift(); // keep only two
@@ -249,7 +284,7 @@ const Map = ( { onMapClick, searchQuery, contentType, setSearchQuery, setSubmitt
249
  console.log("Invalid tool mode:", geoToolMode);
250
  }
251
 
252
- }, [geoToolMode, geoPoints, geoUnit, areaPoints]);
253
 
254
  useEffect(() => {
255
  if (geoPoints.length === 2) {
@@ -655,6 +690,21 @@ const Map = ( { onMapClick, searchQuery, contentType, setSearchQuery, setSubmitt
655
  </Marker>
656
  )}
657
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  {/* Only show geodistance markers/polyline if sidebar is open */}
659
  {geoSidebarOpen && geoToolMode === "distance" && geoPoints.map((pt, index) => (
660
  <Marker key={`geo-${index}`}
@@ -799,7 +849,176 @@ const Map = ( { onMapClick, searchQuery, contentType, setSearchQuery, setSubmitt
799
  </button>
800
  )}
801
 
802
- {/* Geo Sidebar */}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
803
  {geoSidebarOpen && (
804
  <div style={{
805
  position: 'fixed',
 
82
 
83
  const [countryBorders, setCountryBorders] = useState(null);
84
 
85
+ const [explorationMode, setExplorationMode] = useState(false);
86
+ const [explorationRadius, setExplorationRadius] = useState(10000);
87
+ const [explorationLimit, setExplorationLimit] = useState(10);
88
+ const [explorationMarkers, setExplorationMarkers] = useState([]);
89
+ const [explorationSidebarOpen, setExplorationSidebarOpen] = useState(false);
90
+
91
  const CenterMap = ({ position }) => {
92
  const map = useMap();
93
  useEffect(() => {
 
214
  };
215
 
216
  const handleMapClick = useCallback(async (lat, lon) => {
217
+ if (explorationMode) {
218
+ // Handle exploration mode click
219
+ try {
220
+ const res = await fetch(`${BACKEND_URL}/wiki/nearby`, {
221
+ method: 'POST',
222
+ headers: { 'Content-Type': 'application/json' },
223
+ body: JSON.stringify({
224
+ lat: lat,
225
+ lon: lon,
226
+ radius: explorationRadius,
227
+ limit: explorationLimit
228
+ }),
229
+ });
230
+
231
+ if (res.ok) {
232
+ const data = await res.json();
233
+ const markers = data.pages.map(page => ({
234
+ position: [page.lat, page.lon],
235
+ title: page.title,
236
+ distance: page.dist
237
+ }));
238
+ setExplorationMarkers(markers);
239
+ console.log(`Found ${markers.length} nearby pages`);
240
+ } else {
241
+ console.error('Failed to fetch nearby pages');
242
+ }
243
+ } catch (err) {
244
+ console.error('Error fetching nearby pages:', err);
245
+ }
246
+ } else if (geoToolMode === "distance") {
247
  const updatedPoints = [...geoPoints, { lat, lon }];
248
  if (updatedPoints.length > 2) {
249
  updatedPoints.shift(); // keep only two
 
284
  console.log("Invalid tool mode:", geoToolMode);
285
  }
286
 
287
+ }, [explorationMode, explorationRadius, explorationLimit, geoToolMode, geoPoints, geoUnit, areaPoints]);
288
 
289
  useEffect(() => {
290
  if (geoPoints.length === 2) {
 
690
  </Marker>
691
  )}
692
 
693
+ {/* Exploration Mode Markers */}
694
+ {explorationMode && explorationMarkers.map((marker, index) => (
695
+ <Marker
696
+ key={`exploration-${index}`}
697
+ position={marker.position}
698
+ >
699
+ <Popup>
700
+ <div>
701
+ <strong>{marker.title}</strong><br />
702
+ <small>Distance: {marker.distance.toFixed(1)}m</small>
703
+ </div>
704
+ </Popup>
705
+ </Marker>
706
+ ))}
707
+
708
  {/* Only show geodistance markers/polyline if sidebar is open */}
709
  {geoSidebarOpen && geoToolMode === "distance" && geoPoints.map((pt, index) => (
710
  <Marker key={`geo-${index}`}
 
849
  </button>
850
  )}
851
 
852
+ {/* Exploration Mode Button */}
853
+ {!explorationSidebarOpen && !geoSidebarOpen && (
854
+ <button
855
+ onClick={() => setExplorationSidebarOpen(true)}
856
+ style={{
857
+ position: 'absolute',
858
+ top: 50, // Position below Geo Tools button
859
+ right: 12,
860
+ zIndex: 1000,
861
+ padding: '6px 12px',
862
+ backgroundColor: '#4caf50',
863
+ color: 'white',
864
+ border: 'none',
865
+ borderRadius: 4,
866
+ cursor: 'pointer'
867
+ }}
868
+ >
869
+ Exploration
870
+ </button>
871
+ )}
872
+
873
+ {/* Exploration Mode Button - when Geo Tools sidebar is open */}
874
+ {!explorationSidebarOpen && geoSidebarOpen && (
875
+ <button
876
+ onClick={() => setExplorationSidebarOpen(true)}
877
+ style={{
878
+ position: 'fixed',
879
+ top: 320, // Position below Geo Tools sidebar
880
+ right: 24,
881
+ zIndex: 2000,
882
+ padding: '6px 12px',
883
+ backgroundColor: '#4caf50',
884
+ color: 'white',
885
+ border: 'none',
886
+ borderRadius: 4,
887
+ cursor: 'pointer'
888
+ }}
889
+ >
890
+ Exploration
891
+ </button>
892
+ )}
893
+
894
+ {/* Exploration Sidebar */}
895
+ {explorationSidebarOpen && (
896
+ <div style={{
897
+ position: 'fixed',
898
+ top: geoSidebarOpen ? 320 : 24, // Position below Geo Tools sidebar if open
899
+ right: 24,
900
+ width: 280,
901
+ background: 'white',
902
+ borderRadius: 10,
903
+ boxShadow: '0 2px 12px rgba(0,0,0,0.12)',
904
+ zIndex: 2000,
905
+ padding: 20,
906
+ display: 'flex',
907
+ flexDirection: 'column',
908
+ gap: 16,
909
+ border: '1px solid #eee'
910
+ }}>
911
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
912
+ <strong>Exploration Mode</strong>
913
+ <button
914
+ onClick={() => {
915
+ setExplorationSidebarOpen(false);
916
+ setExplorationMode(false);
917
+ setExplorationMarkers([]);
918
+ }}
919
+ style={{
920
+ background: 'none',
921
+ border: 'none',
922
+ fontSize: 18,
923
+ cursor: 'pointer',
924
+ color: '#888'
925
+ }}
926
+ title="Close"
927
+ >×</button>
928
+ </div>
929
+
930
+ <div>
931
+ <label style={{ fontWeight: 500, marginBottom: 8, display: 'block' }}>
932
+ Search Radius (meters):
933
+ </label>
934
+ <input
935
+ type="range"
936
+ min="10"
937
+ max="10000"
938
+ step="1000"
939
+ value={explorationRadius}
940
+ onChange={(e) => setExplorationRadius(parseInt(e.target.value))}
941
+ style={{ width: '100%' }}
942
+ />
943
+ <div style={{ textAlign: 'center', marginTop: 4 }}>
944
+ {explorationRadius.toLocaleString()}m
945
+ </div>
946
+ </div>
947
+
948
+ <div>
949
+ <label style={{ fontWeight: 500, marginBottom: 8, display: 'block' }}>
950
+ Number of Results:
951
+ </label>
952
+ <input
953
+ type="range"
954
+ min="1"
955
+ max="50"
956
+ step="1"
957
+ value={explorationLimit}
958
+ onChange={(e) => setExplorationLimit(parseInt(e.target.value))}
959
+ style={{ width: '100%' }}
960
+ />
961
+ <div style={{ textAlign: 'center', marginTop: 4 }}>
962
+ {explorationLimit} results
963
+ </div>
964
+ </div>
965
+
966
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
967
+ <input
968
+ type="checkbox"
969
+ id="explorationMode"
970
+ checked={explorationMode}
971
+ onChange={(e) => setExplorationMode(e.target.checked)}
972
+ />
973
+ <label htmlFor="explorationMode" style={{ fontWeight: 500 }}>
974
+ Enable Exploration Mode
975
+ </label>
976
+ </div>
977
+
978
+ {explorationMode && (
979
+ <div style={{
980
+ padding: '8px 12px',
981
+ backgroundColor: '#e8f5e8',
982
+ borderRadius: 4,
983
+ fontSize: '14px',
984
+ color: '#2e7d32'
985
+ }}>
986
+ ✓ Click anywhere on the map to find nearby Wikipedia pages
987
+ </div>
988
+ )}
989
+
990
+ {explorationMarkers.length > 0 && (
991
+ <div style={{
992
+ padding: '8px 12px',
993
+ backgroundColor: '#e3f2fd',
994
+ borderRadius: 4,
995
+ fontSize: '14px',
996
+ color: '#1976d2'
997
+ }}>
998
+ Found {explorationMarkers.length} nearby pages
999
+ </div>
1000
+ )}
1001
+
1002
+ <button
1003
+ onClick={() => {
1004
+ setExplorationMarkers([]);
1005
+ }}
1006
+ style={{
1007
+ padding: '6px 0',
1008
+ borderRadius: 4,
1009
+ border: '1px solid #f44336',
1010
+ background: '#f44336',
1011
+ color: 'white',
1012
+ fontWeight: 500,
1013
+ cursor: 'pointer'
1014
+ }}
1015
+ >
1016
+ Clear Markers
1017
+ </button>
1018
+ </div>
1019
+ )}
1020
+
1021
+ {/* Geo Sidebar - Keep as is */}
1022
  {geoSidebarOpen && (
1023
  <div style={{
1024
  position: 'fixed',
main.py CHANGED
@@ -22,6 +22,12 @@ class Geodistance(BaseModel):
22
  lon2: float = Field(..., ge=-180, le=180)
23
  unit: str = "km"
24
 
 
 
 
 
 
 
25
  app.add_middleware(
26
  CORSMiddleware,
27
  allow_origins=["*"], # Replace with your frontend domain in prod
@@ -151,11 +157,68 @@ def get_geodistance(payload: Geodistance):
151
  status_code=200
152
  )
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  @app.get("/random")
155
  def random():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  return JSONResponse(
157
  content={
158
- "message": "Spare endpoint to test."
 
159
  },
160
  status_code=200
161
  )
 
22
  lon2: float = Field(..., ge=-180, le=180)
23
  unit: str = "km"
24
 
25
+ class NearbyWikiPage(BaseModel):
26
+ lat: float = Field(default=54.163337, ge=-90, le=90)
27
+ lon: float = Field(default=37.561109, ge=-180, le=180)
28
+ radius: int = Field(default=1000, ge=10, le=10000,description="Distance in meters from the reference point")
29
+ limit: int = Field(10, ge=1, description="Number of pages to return")
30
+
31
  app.add_middleware(
32
  CORSMiddleware,
33
  allow_origins=["*"], # Replace with your frontend domain in prod
 
157
  status_code=200
158
  )
159
 
160
+ @app.post("/wiki/nearby")
161
+ async def get_nearby_wiki_pages(payload: NearbyWikiPage):
162
+ lat, lon = payload.lat, payload.lon
163
+ radius = payload.radius
164
+ limit = payload.limit
165
+
166
+ url = ("https://en.wikipedia.org/w/api.php"+"?action=query"
167
+ "&list=geosearch"
168
+ f"&gscoord={lat}|{lon}"
169
+ f"&gsradius={radius}"
170
+ f"&gslimit={limit}"
171
+ "&format=json")
172
+ print(url)
173
+ try:
174
+ response = requests.get(url, timeout=10)
175
+ if response.status_code != 200:
176
+ return JSONResponse(
177
+ content={"error": "Failed to fetch nearby pages"},
178
+ status_code=500
179
+ )
180
+ data = response.json()
181
+
182
+ pages = data.get("query", {}).get("geosearch", [])
183
+
184
+ return JSONResponse(
185
+ content={
186
+ "pages": pages,
187
+ "count": len(pages)
188
+ },
189
+ status_code=200
190
+ )
191
+ except Exception as e:
192
+ return JSONResponse(
193
+ content={"error": str(e)},
194
+ status_code=500
195
+ )
196
+
197
+
198
+
199
+
200
  @app.get("/random")
201
  def random():
202
+ url = "https://en.wikipedia.org/w/api.php?action=query&list=geosearch&gscoord=54.163337|37.561109&gsradius=10000&gslimit=10&format=json"
203
+ response = requests.get(url, timeout=10)
204
+
205
+ if response.status_code != 200:
206
+ return JSONResponse(
207
+ content={"error": "Failed to fetch random page"},
208
+ status_code=500
209
+ )
210
+ data = response.json()
211
+ pages = data.get("query", {}).get("geosearch", [])
212
+ if not pages:
213
+ return JSONResponse(
214
+ content={"error": "No pages found"},
215
+ status_code=404
216
+ )
217
+
218
  return JSONResponse(
219
  content={
220
+ "pages": pages,
221
+ "count": len(pages)
222
  },
223
  status_code=200
224
  )