মাঝে মাঝে, আপনি আপনার অ্যাপ্লিকেশনের মধ্যে ভূ-স্থানিক ফাংশন সম্পাদন করার প্রয়োজনের সম্মুখীন হতে পারেন, যেমন ব্যবহারকারীর অবস্থান ম্যাপ করা বা ভৌগলিক ডেটা বিশ্লেষণ করা। এই কাজের জন্য অসংখ্য ভাষা-নির্দিষ্ট লাইব্রেরি উপলব্ধ, যেমন GDAL, Shapely, এবং Geopandas-এর জন্য Python।
বিকল্পভাবে, ভূ-স্থানিক কার্যকারিতা ডাটাবেসের মাধ্যমে প্রয়োগ করা যেতে পারে; উদাহরণ স্বরূপ, আপনি PostgreSQL এর মত একটি রিলেশনাল ডাটাবেসের সাথে PostGIS এক্সটেনশন ব্যবহার করতে পারেন, অথবা Azure CosmosDB এর মত ডিস্ট্রিবিউটেড ডাটাবেসে স্থানিক ডাটা টাইপের জন্য নেটিভ সমর্থন লাভ করতে পারেন।
যাইহোক, যদি আপনার ব্যাকএন্ড স্টোরেজ, যেমন Redis বা Google Spanner, স্থানীয়ভাবে স্থানিক প্রশ্নগুলিকে সমর্থন না করে এবং আপনাকে বড় আকারের ভূ-স্থানিক প্রশ্নগুলি পরিচালনা করতে হবে, এই নিবন্ধটি আপনার জন্য তৈরি করা হয়েছে৷
স্থানিক ডেটা পরিচালনা করার জন্য আপনি সর্বদা অন্য একটি মাইক্রোসার্ভিস তৈরি করতে পারেন, তবে এই বিকল্পটি প্রায়শই একটি অতিরিক্ত অ্যাপ্লিকেশন বজায় রাখার ওভারহেডকে জড়িত করে। আরেকটি পদ্ধতি হল জিও-ইনডেক্সিং লাইব্রেরি যেমন S2 এবং H3 ব্যবহার করা। S2, Google দ্বারা বিকশিত, হিলবার্ট বক্ররেখার উপর ভিত্তি করে, যখন H3, উবার দ্বারা উন্নত, একটি জিওডেসিক বিচ্ছিন্ন গ্লোবাল গ্রিড সিস্টেমের উপর ভিত্তি করে। S2 এবং H3 অনেক মিল শেয়ার করে: উভয়ই একটি প্রদত্ত অঞ্চলকে কোষে ভাগ করে এবং এই কোষগুলিকে সূচক করতে 64-বিট পূর্ণসংখ্যা ব্যবহার করে।
যাইহোক, প্রধান পার্থক্য কোষের আকারের মধ্যে রয়েছে; S2 বর্গাকার-আকৃতির কোষ ব্যবহার করে, যেখানে H3 ষড়ভুজ-আকৃতির কোষ ব্যবহার করে। কিছু অ্যাপ্লিকেশনের জন্য, H3 আরও ভাল পারফরম্যান্স অফার করতে পারে। যাইহোক, সামগ্রিকভাবে, উভয় লাইব্রেরি যথেষ্ট হওয়া উচিত। এই নিবন্ধে, আমরা S2 ব্যবহার করব, কিন্তু আপনি H3 ব্যবহার করে অনুরূপ ফাংশন সম্পাদন করতে পারেন।
কোষের স্তর: শ্রেণিবিন্যাস বিভিন্ন স্তরের বিশদ বিবরণের অনুমতি দেয়, বড় অঞ্চল থেকে ছোট সুনির্দিষ্ট এলাকা পর্যন্ত। প্রতিটি স্তর একটি ভিন্ন রেজোলিউশন প্রতিনিধিত্ব করে:
স্তর 0: বৃহত্তম কোষ, পৃথিবীর পৃষ্ঠের একটি উল্লেখযোগ্য অংশ জুড়ে।
উচ্চ স্তর: কোষগুলি ধীরে ধীরে ছোট চতুর্ভুজে বিভক্ত হয়। উদাহরণ স্বরূপ, লেভেল 1 সেল প্রত্যেকটি চারটি লেভেল 2 কক্ষে বিভক্ত, ইত্যাদি।
রেজোলিউশন এবং ক্ষেত্রফল: উচ্চতর স্তরগুলি সূক্ষ্ম রেজোলিউশন এবং ছোট কোষ অঞ্চলগুলির সাথে মিলে যায়। এই শ্রেণিবিন্যাস সুনির্দিষ্ট সূচীকরণ এবং বিশদ স্তরের বিভিন্ন স্তরে অনুসন্ধানের অনুমতি দেয়।
নীচের সারণীটি তাদের সংশ্লিষ্ট এলাকার সাথে বিভিন্ন কোষের স্তর দেখায়।
স্তর | ন্যূনতম এলাকা | সর্বোচ্চ এলাকা | গড় এলাকা | ইউনিট | কক্ষের সংখ্যা |
---|---|---|---|---|---|
00 | 85011012.19 | 85011012.19 | 85011012.19 | km2 | 6 |
01 | 21252753.05 | 21252753.05 | 21252753.05 | km2 | 24 |
02 | 4919708.23 | 6026521.16 | 5313188.26 | km2 | 96 |
03 | 1055377.48 | 1646455.50 | 1328297.07 | km2 | 384 |
04 | 231564.06 | 413918.15 | 332074.27 | km2 | 1536 |
05 | 53798.67 | 104297.91 | 83018.57 | km2 | 6K |
06 | 12948.81 | 26113.30 | 20754.64 | km2 | 24K |
07 | 3175.44 | 6529.09 | 5188.66 | km2 | 98K |
08 | 786.20 | 1632.45 | 1297.17 | km2 | 393K |
09 | 195.59 | 408.12 | 324.29 | km2 | 1573K |
10 | ৪৮.৭৮ | 102.03 | ৮১.০৭ | km2 | 6M |
11 | 12.18 | 25.51 | 20.27 | km2 | 25M |
12 | ৩.০৪ | ৬.৩৮ | ৫.০৭ | km2 | 100M |
13 | 0.76 | 1.59 | 1.27 | km2 | 402M |
14 | 0.19 | 0.40 | 0.32 | km2 | 1610M |
15 | 47520.30 | 99638.93 | 79172.67 | m2 | 6B |
16 | 11880.08 | 24909.73 | 19793.17 | m2 | 25B |
17 | 2970.02 | 6227.43 | 4948.29 | m2 | 103B |
18 | 742.50 | 1556.86 | 1237.07 | m2 | 412B |
19 | 185.63 | 389.21 | 309.27 | m2 | 1649B |
20 | 46.41 | 97.30 | 77.32 | m2 | 7টি |
21 | 11.60 | 24.33 | 19.33 | m2 | 26টি |
22 | 2.90 | ৬.০৮ | 4.83 | m2 | 105T |
23 | 0.73 | 1.52 | 1.21 | m2 | 422T |
24 | 0.18 | 0.38 | 0.30 | m2 | 1689T |
25 | 453.19 | 950.23 | 755.05 | cm2 | 7e15 |
26 | 113.30 | 237.56 | 188.76 | cm2 | 27e15 |
27 | 28.32 | ৫৯.৩৯ | 47.19 | cm2 | 108e15 |
28 | 7.08 | 14.85 | 11.80 | cm2 | 432e15 |
29 | 1.77 | 3.71 | 2.95 | cm2 | 1729e15 |
30 | 0.44 | 0.93 | 0.74 | cm2 | 7e18 |
প্রদত্ত টেবিল থেকে, এটা স্পষ্ট যে আপনি S2 ব্যবহার করে 0.44 সেমি^2 পর্যন্ত ম্যাপিং নির্ভুলতা অর্জন করতে পারেন। একটি S2 কক্ষের প্রতিটি বর্গক্ষেত্রের মধ্যে, একটি শিশু কোষ রয়েছে যা একই অভিভাবককে ভাগ করে, যা একটি শ্রেণিবদ্ধ কাঠামো নির্দেশ করে। ঘরের স্তর একটি স্থির মান হতে পারে (সমস্ত কক্ষে একই স্তর প্রয়োগ করা হয়) বা গতিশীল হতে পারে যেখানে S2 সিদ্ধান্ত নেয় কোন রেজোলিউশনটি সবচেয়ে ভাল কাজ করে৷
একটি উদাহরণ দিয়ে শুরু করা যাক। বিবেচনা করুন আমরা একটি অ্যাপ্লিকেশন লিখছি যা সিয়াটেল এলাকার জন্য প্রক্সিমিটি পরিষেবার মতো বৈশিষ্ট্যগুলি প্রদান করে৷ আমরা প্রদত্ত আশেপাশে কফি শপগুলির একটি তালিকা ফেরত দিতে চাই৷ এই ক্রিয়াকলাপগুলি সম্পাদন করার জন্য, আমরা এই কাজটিকে 4টি সাব-টাস্কে ভাগ করব:
একটি Google মানচিত্র লোড করতে, আমরা gmplot লাইব্রেরি ব্যবহার করব। এই লাইব্রেরির লোড করার জন্য একটি Google Maps API কী প্রয়োজন৷ API কী তৈরি করতে, এখানে নির্দেশাবলী অনুসরণ করুন।
import gmplot import const # plot seattle with zoom level 13 gmap = gmplot.GoogleMapPlotter(47.6061, -122.3328, 13, apikey=const.API_KEY) # Draw the map to an HTML file: gmap.draw('map.html')
উপরের কোডটি নীচে দেখানো হিসাবে একটি map.html ফাইল তৈরি করে:
এখন আমাদের কাছে মানচিত্র আছে, আসুন মানচিত্রের জন্য কিছু S2 ঘর আঁকুন:
from s2 import * import gmplot # plot seattle with zoom level 13 gmap = gmplot.GoogleMapPlotter(47.6061, -122.3328, 13, apikey=const.API_KEY) areatobeplotted = [ (47.64395531736767,-122.43597221319135), (47.51369277846956,-122.43597221319135), (47.51369277846956,-122.24156866779164), (47.64395531736767,-122.24156866779164), (47.64395531736767,-122.43597221319135) ] region_rect = S2LatLngRect( S2LatLng.FromDegrees(47.51369277846956,-122.43597221319135), S2LatLng.FromDegrees(47.64395531736767, -122.24156866779164)) coverer = S2RegionCoverer() coverer.set_min_level(8) coverer.set_max_level(15) covering = coverer.GetCovering(region_rect) geoms = 0 for cellid in covering: new_cell = S2Cell(cellid) vertices = [] for i in range(0, 4): vertex = new_cell.GetVertex(i) latlng = S2LatLng(vertex) vertices.append((latlng.lat().degrees(), latlng.lng().degrees())) gmap.polygon(*zip(*vertices), face_color='pink', edge_color='cornflowerblue', edge_width=5) geoms+=1 gmap.polygon(*zip(*areatobeplotted), face_color='red', edge_color='green', edge_width=5) print(f"Total Geometries: {geoms}") gmap.draw('/tmp/map.html')
Output: Total Geometries: 273
উপরের কোডে, আমরা প্রথমে গুগল ম্যাপ প্লটারকে সিয়াটেল এলাকার চারপাশে কেন্দ্রীভূত করি। S2RegionCoverer
এ, আমরা সর্বনিম্ন স্তর 8 এবং সর্বোচ্চ 15-এর মধ্যে গতিশীল স্তরের জন্য অঞ্চল কভারারকে আরম্ভ করি৷ এটি S2 কে সর্বোত্তম ফিট করার জন্য গতিশীলভাবে সমস্ত কোষকে নির্দিষ্ট কোষের আকারে ফিট করতে দেয়৷ GetCovering
পদ্ধতি সিয়াটেল এলাকার চারপাশে একটি আয়তক্ষেত্রের জন্য আবরণ প্রদান করে।
তারপর, আমরা প্রতিটি কক্ষের উপর পুনরাবৃত্তি করি, কোষগুলির জন্য শীর্ষবিন্দুগুলি গণনা করি এবং সেগুলি মানচিত্রে প্লট করি৷ আমরা তৈরি করা কোষের সংখ্যা প্রায় 273 রাখি। অবশেষে, আমরা ইনপুট আয়তক্ষেত্রটিকে লাল রঙে প্লট করি। এই কোডটি /tmp/map.html
এ সিয়াটেল মানচিত্রে S2 কোষগুলিকে প্লট করবে, যেমনটি নীচে দেখানো হয়েছে:
আসুন তাদের S2 সেল আইডেন্টিফায়ার সহ কফি শপের একটি ডাটাবেস তৈরি করি। আপনি আপনার পছন্দের যেকোনো ডাটাবেসে এই সেলগুলি সংরক্ষণ করতে পারেন। এই টিউটোরিয়ালের জন্য, আমরা একটি SQLite ডেটাবেস ব্যবহার করব। নীচের কোড নমুনায়, আমরা 3টি ক্ষেত্র Id
, name
, এবং cell_id
সহ একটি টেবিল CoffeeShops
তৈরি করতে SQLite ডাটাবেসের সাথে সংযোগ করি।
পূর্ববর্তী উদাহরণের মতো, আমরা কোষগুলি গণনা করতে S2RegionCoverer
ব্যবহার করি কিন্তু এইবার, আমরা প্লটিং পয়েন্টের জন্য একটি নির্দিষ্ট স্তর ব্যবহার করি। অবশেষে, গণনা করা আইডি একটি স্ট্রিং এ রূপান্তরিত হয় এবং ডাটাবেসে সংরক্ষণ করা হয়।
import sqlite3 from s2 import S2CellId,S2LatLng,S2RegionCoverer # Connect to SQLite database conn = sqlite3.connect('/tmp/sqlite_cells.db') cursor = conn.cursor() # Create a table to store cell IDs cursor.execute('''CREATE TABLE IF NOT EXISTS CoffeeShops ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, cell_id TEXT )''') coverer = S2RegionCoverer() # Function to generate S2 cell ID for a given latitude and longitude def generate_cell_id(latitude, longitude, level=16): cell=S2CellId(S2LatLng.FromDegrees(latitude, longitude)) return str(cell.parent(level)) # Function to insert cell IDs into the database def insert_cell_ids(name,lat,lng): cell_id = generate_cell_id(lat, lng) cursor.execute("INSERT INTO CoffeeShops (name, cell_id) VALUES (?, ?)", (name, cell_id)) conn.commit() # Insert cell IDs into the database insert_cell_ids("Overcast Coffee", 47.616656277302155, -122.31156460382837) insert_cell_ids("Seattle Sunshine", 47.67366852914391, -122.29051997415843) insert_cell_ids("Sip House", 47.6682364706238, -122.31328618043693) insert_cell_ids("Victoria Coffee",47.624408595334536, -122.3117362652041) # Close connection conn.close()
এই মুহুর্তে, আমাদের কাছে একটি ডাটাবেস রয়েছে যা কফি শপগুলিকে তাদের সেল আইডি সহ সঞ্চয় করে, সেল স্তরের জন্য নির্বাচিত রেজোলিউশন দ্বারা নির্ধারিত৷
অবশেষে, ইউনিভার্সিটি ডিস্ট্রিক্ট এলাকায় কফি শপের জন্য প্রশ্ন করা যাক।
import sqlite3 from s2 import S2RegionCoverer,S2LatLngRect, S2LatLng # Connect to SQLite database conn = sqlite3.connect('/tmp/sqlite_cells.db') cursor = conn.cursor() # Function to query database for cells intersecting with the given polygon def query_intersecting_cells(start_x,start_y,end_x,end_y): # Create S2RegionCoverer region_rect = S2LatLngRect( S2LatLng.FromDegrees(start_x,start_y), S2LatLng.FromDegrees(end_x,end_y)) coverer = S2RegionCoverer() coverer.set_min_level(8) coverer.set_max_level(15) covering = coverer.GetCovering(region_rect) # Query for intersecting cells intersecting_cells = set() for cell_id in covering: cursor.execute("SELECT name FROM CoffeeShops WHERE cell_id >= ? and cell_id<=?", (str(cell_id.range_min()),str(cell_id.range_max()),)) intersecting_cells.update(cursor.fetchall()) return intersecting_cells # Query for intersecting cells intersecting_cells = query_intersecting_cells(47.6527847,-122.3286438,47.6782181, -122.2797203) # Print intersecting cells print("Intersecting cells:") for cell_id in intersecting_cells: print(cell_id[0]) # Close connection conn.close()
Output: Intersecting cells: Sip House Seattle Sunshine
নীচে কোষগুলির একটি ভিজ্যুয়াল উপস্থাপনা রয়েছে। সংক্ষিপ্ততা বজায় রাখার জন্য, নীচের জন্য ভিজ্যুয়ালাইজেশন কোড যোগ করা হয়নি।
যেহেতু সমস্ত শিশু এবং পিতামাতার কোষ একটি উপসর্গ ভাগ করে, তাই আমরা এই দুটি মানের মধ্যে সমস্ত ঘর পেতে সর্বনিম্ন এবং সর্বোচ্চ এর মধ্যে সেল রেঞ্জের জন্য অনুসন্ধান করতে পারি। আমাদের উদাহরণে, আমরা কফি শপ জিজ্ঞাসা করতে একই নীতি ব্যবহার করি
এই নিবন্ধে, আমরা দেখিয়েছি যে কীভাবে জিও-ইনডেক্সিং ব্যবহার করতে হয় এমন ডেটাবেসে ভূ-স্থানিক ডেটা সঞ্চয় এবং অনুসন্ধান করতে হয় যা ভূ-স্থানিক অনুসন্ধান সমর্থন করে না। এটি একাধিক ব্যবহারের ক্ষেত্রে আরও বাড়ানো যেতে পারে যেমন 2 পয়েন্টের মধ্যে রাউটিং গণনা করা বা নিকটতম প্রতিবেশীদের পাওয়া।
সাধারণত, জিও-ইনডেক্সড ডাটাবেস অনুসন্ধানের জন্য, আপনাকে ডেটাতে কিছু অতিরিক্ত পোস্ট-প্রসেসিং করতে হবে। আমরা যাতে নোডকে অভিভূত না করি তা নিশ্চিত করার জন্য অনুসন্ধান এবং পোস্ট-প্রসেসিং যুক্তির জন্য যত্নশীল বিবেচনার প্রয়োজন।