ভূমিকা: মাঝে মাঝে, আপনি আপনার অ্যাপ্লিকেশনের মধ্যে ভূ-স্থানিক ফাংশন সম্পাদন করার প্রয়োজনের সম্মুখীন হতে পারেন, যেমন ব্যবহারকারীর অবস্থান ম্যাপ করা বা ভৌগলিক ডেটা বিশ্লেষণ করা। এই কাজের জন্য অসংখ্য ভাষা-নির্দিষ্ট লাইব্রেরি উপলব্ধ, যেমন GDAL, Shapely, এবং Geopandas-এর জন্য Python। বিকল্পভাবে, ভূ-স্থানিক কার্যকারিতা ডাটাবেসের মাধ্যমে প্রয়োগ করা যেতে পারে; উদাহরণ স্বরূপ, আপনি PostgreSQL এর মত একটি রিলেশনাল ডাটাবেসের সাথে PostGIS এক্সটেনশন ব্যবহার করতে পারেন, অথবা Azure CosmosDB এর মত ডিস্ট্রিবিউটেড ডাটাবেসে স্থানিক ডাটা টাইপের জন্য নেটিভ সমর্থন লাভ করতে পারেন। যাইহোক, যদি আপনার ব্যাকএন্ড স্টোরেজ, যেমন Redis বা Google Spanner, স্থানীয়ভাবে স্থানিক প্রশ্নগুলিকে সমর্থন না করে এবং আপনাকে বড় আকারের ভূ-স্থানিক প্রশ্নগুলি পরিচালনা করতে হবে, এই নিবন্ধটি আপনার জন্য তৈরি করা হয়েছে৷ আমার বিকল্প কি? স্থানিক ডেটা পরিচালনা করার জন্য আপনি সর্বদা অন্য একটি মাইক্রোসার্ভিস তৈরি করতে পারেন, তবে এই বিকল্পটি প্রায়শই একটি অতিরিক্ত অ্যাপ্লিকেশন বজায় রাখার ওভারহেডকে জড়িত করে। আরেকটি পদ্ধতি হল জিও-ইনডেক্সিং লাইব্রেরি যেমন S2 এবং H3 ব্যবহার করা। S2, Google দ্বারা বিকশিত, হিলবার্ট বক্ররেখার উপর ভিত্তি করে, যখন H3, উবার দ্বারা উন্নত, একটি জিওডেসিক বিচ্ছিন্ন গ্লোবাল গ্রিড সিস্টেমের উপর ভিত্তি করে। S2 এবং H3 অনেক মিল শেয়ার করে: উভয়ই একটি প্রদত্ত অঞ্চলকে কোষে ভাগ করে এবং এই কোষগুলিকে সূচক করতে 64-বিট পূর্ণসংখ্যা ব্যবহার করে। যাইহোক, প্রধান পার্থক্য কোষের আকারের মধ্যে রয়েছে; S2 বর্গাকার-আকৃতির কোষ ব্যবহার করে, যেখানে H3 ষড়ভুজ-আকৃতির কোষ ব্যবহার করে। কিছু অ্যাপ্লিকেশনের জন্য, H3 আরও ভাল পারফরম্যান্স অফার করতে পারে। যাইহোক, সামগ্রিকভাবে, উভয় লাইব্রেরি যথেষ্ট হওয়া উচিত। এই নিবন্ধে, আমরা S2 ব্যবহার করব, কিন্তু আপনি H3 ব্যবহার করে অনুরূপ ফাংশন সম্পাদন করতে পারেন। Google S2 লাইব্রেরির মৌলিক ধারণা কোষ: S2 গোলকটিকে কোষে বিভক্ত করে, প্রতিটি একটি অনন্য 64-বিট শনাক্তকারী সহ। কোষের স্তর: শ্রেণিবিন্যাস বিভিন্ন স্তরের বিশদ বিবরণের অনুমতি দেয়, বড় অঞ্চল থেকে ছোট সুনির্দিষ্ট এলাকা পর্যন্ত। প্রতিটি স্তর একটি ভিন্ন রেজোলিউশন প্রতিনিধিত্ব করে: স্তর 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টি সাব-টাস্কে ভাগ করব: সিয়াটেল মানচিত্র লোড হচ্ছে সিয়াটেল মানচিত্রে S2 কক্ষগুলিকে কল্পনা করুন৷ ডাটাবেসে কয়েকটি কফি শপের অবস্থান সংরক্ষণ করুন নিকটস্থ কফি শপ জন্য ক্যোয়ারী সিয়াটেল মানচিত্র লোড হচ্ছে একটি 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 সেলগুলিকে কল্পনা করুন৷ এখন আমাদের কাছে মানচিত্র আছে, আসুন মানচিত্রের জন্য কিছু 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 উপরের কোডে, আমরা প্রথমে গুগল ম্যাপ প্লটারকে সিয়াটেল এলাকার চারপাশে কেন্দ্রীভূত করি। এ, আমরা সর্বনিম্ন স্তর 8 এবং সর্বোচ্চ 15-এর মধ্যে গতিশীল স্তরের জন্য অঞ্চল কভারারকে আরম্ভ করি৷ এটি S2 কে সর্বোত্তম ফিট করার জন্য গতিশীলভাবে সমস্ত কোষকে নির্দিষ্ট কোষের আকারে ফিট করতে দেয়৷ পদ্ধতি সিয়াটেল এলাকার চারপাশে একটি আয়তক্ষেত্রের জন্য আবরণ প্রদান করে। S2RegionCoverer GetCovering তারপর, আমরা প্রতিটি কক্ষের উপর পুনরাবৃত্তি করি, কোষগুলির জন্য শীর্ষবিন্দুগুলি গণনা করি এবং সেগুলি মানচিত্রে প্লট করি৷ আমরা তৈরি করা কোষের সংখ্যা প্রায় 273 রাখি। অবশেষে, আমরা ইনপুট আয়তক্ষেত্রটিকে লাল রঙে প্লট করি। এই কোডটি এ সিয়াটেল মানচিত্রে S2 কোষগুলিকে প্লট করবে, যেমনটি নীচে দেখানো হয়েছে: /tmp/map.html ডাটাবেসে কয়েকটি কফি শপের অবস্থান সংরক্ষণ করুন আসুন তাদের S2 সেল আইডেন্টিফায়ার সহ কফি শপের একটি ডাটাবেস তৈরি করি। আপনি আপনার পছন্দের যেকোনো ডাটাবেসে এই সেলগুলি সংরক্ষণ করতে পারেন। এই টিউটোরিয়ালের জন্য, আমরা একটি SQLite ডেটাবেস ব্যবহার করব। নীচের কোড নমুনায়, আমরা 3টি ক্ষেত্র , , এবং সহ একটি টেবিল তৈরি করতে SQLite ডাটাবেসের সাথে সংযোগ করি। Id name cell_id CoffeeShops পূর্ববর্তী উদাহরণের মতো, আমরা কোষগুলি গণনা করতে ব্যবহার করি কিন্তু এইবার, আমরা প্লটিং পয়েন্টের জন্য একটি নির্দিষ্ট স্তর ব্যবহার করি। অবশেষে, গণনা করা আইডি একটি স্ট্রিং এ রূপান্তরিত হয় এবং ডাটাবেসে সংরক্ষণ করা হয়। 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 পয়েন্টের মধ্যে রাউটিং গণনা করা বা নিকটতম প্রতিবেশীদের পাওয়া। সাধারণত, জিও-ইনডেক্সড ডাটাবেস অনুসন্ধানের জন্য, আপনাকে ডেটাতে কিছু অতিরিক্ত পোস্ট-প্রসেসিং করতে হবে। আমরা যাতে নোডকে অভিভূত না করি তা নিশ্চিত করার জন্য অনুসন্ধান এবং পোস্ট-প্রসেসিং যুক্তির জন্য যত্নশীল বিবেচনার প্রয়োজন। তথ্যসূত্র: গুগল ম্যাপ প্লটার - https://github.com/gmplot/gmplot/wiki/GoogleMapPlotter S2 বিকাশকারী নির্দেশিকা - http://s2geometry.io/devguide/ Sqlite - https://docs.python.org/3/library/sqlite3.html Azure Cosmos DB geospatial - https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/geospatial?tabs=javascript GDAL - https://gdal.org/index.html শ্যাপলি- https://shapely.readthedocs.io/en/stable/manual.html জিওপান্ডাস - https://geopandas.org পোজিস - https://postgis.net/ Google স্প্যানার - https://cloud.google.com/spanner?hl=en রেডিস - https://redis.io/