آماگ
۱۵ بهمن ۱۴۰۴، ۰۷:۵۵

ایجاد پلی‌گون‌های برداری (Vector Polygons) با عملکرد رندر مانند GISCloud

من به دنبال یک راه‌حل مطمئن بوده‌ام که به من امکان دهد یک نقشه وب بسازم و پلی‌گون‌های برداری را بدون صرف وقت زیاد برای بارگذاری چنین داده‌ای روی آن قرار دهم، با این هدف که بتوانم هر پلی‌گون را در رویداد هاور (بالا بردن نشانگر) به رنگ متفاوتی نمایش دهم. تا جایی که می‌دانم 3 گزینه مشخص برای رسیدن به این هدف از طریق کانواس (Canvas)، SVG یا فلش وجود دارد. به نظر می‌رسد فلش بهترین راه‌حل باشد اگر روی آیفون/آیپدهای اپل کار می‌کرد، زیرا سریع‌ترین رندر و نمایش تمیز را ارائه می‌دهد. به نظر می‌رسد کانواس دومین انتخاب خوب باشد اما اگر صدها پلی‌گون روی نقشه نمایش داده شود زمان بسیار زیادی می‌برد در حالی که SVG حتی زمان بیشتری برای رندر نیاز دارد. تقریباً امیدم را برای یافتن راه‌حلی برای این مشکل از دست داده بودم اما امروز با شرکتی به نام GISCloud روبرو شدم http://www.giscloud.com (در حال حاضر در نسخه بتا با ثبت‌نام رایگان). این شرکت به نحوی موفق شده راهی شگفت‌انگیز برای رندر صدها بردار روی نقشه تقریباً در زمان واقعی بیابد. من از رویکرد آن‌ها شگفت‌زده شدم و سوال من از جامعه درباره این است که چگونه می‌توانیم رویکرد آن‌ها را برای استفاده با فناوری‌های موجود مانند لیفلت، اوپن‌لیزرز، وکس... تکرار کنیم. خودتان با دیدن این دمو شگفت‌انگیز نگاه کنید: http://www.giscloud.com/map/284/africa مطمئن شوید روی هر یک از پلی‌گون‌های صفحه هاور می‌کنید و کنترل‌های زوم را آزمایش می‌کنید تا ببینید این پلی‌گون‌ها واقعاً بردار هستند. آنچه با نگاه کردن به درخواست‌ها با فایرباگ متوجه شده‌ام این است که نقشه فایل‌های json خاصی را درخواست می‌کند. به نظر می‌رسد بسته به سطح زوم/ناحیه، چندین فایل json درخواست می‌شود. باید اینجا هم ذکر کنم که وقتی گیس‌کلود داده‌ها را روی صفحه بارگذاری می‌کند، هاور کردن روی یک بردار بلافاصله رنگ را بدون ایجاد درخواست جدید تغییر می‌دهد. مثال‌ها: http://cft1.giscloud.com/t/1316509973/map284/layer1156/3/3/3.json http://cft1.giscloud.com/t/1316509973/map284/layer1156/3/5/3.json http://cft1.giscloud.com/t/1316509973/map284/layer1156/3/4/4.json http://cft1.giscloud.com/t/1316509973/map284/layer1156/3/3/4.json http://cft1.giscloud.com/t/1316509973/map284/layer1156/3/5/4.json من فرض می‌کنم ساختار URL از منطق استاندارد سرویس کاشی (tiling) پیروی می‌کند (مثلاً پوشه سوم از آخر بودن سطح زوم...). در هر صورت من داده‌های واقعی این فایل‌های json را تحلیل کرده‌ام و به نظر می‌رسد منطقی که آن‌ها استفاده می‌کنند نوعی منطق است که توسط آن بردارهای خود را فقط بر اساس این مقادیر داده‌ای می‌سازند: عرض/ارتفاع: آن‌ها عرض و ارتفاع داده‌ای را که در هر درخواست json ارائه می‌شود تعریف می‌کنند پیکسل‌ها: در اینجا مقادیر پیکسلی تعریف می‌کنند که من فرض می‌کنم به نحوی به مختصات پیکسل عمومی x/y برای سطح‌های نقطه‌ای تعمیم‌یافته مرتبط است؟ حدس می‌زنم آن‌ها به نحوی راهی برای ساده‌سازی خودکار منطقه بسته به سطح زوم دارند. با فرض استفاده از مختصات پیکسل حدس می‌زنم آن‌ها به طور چشمگیری اندازه داده‌ای را که باید بارگذاری شود در مقایسه با داده‌های طول/عرض جغرافیایی کاهش می‌دهند. سبک‌ها: در اینجا دو مقدار CSS مربوط به RGB تعریف می‌کنند. «F» نشان‌دهنده رنگ فایل پلی‌گون و «S» نشان‌دهنده رنگ حاشیه پلی‌گون است. geom: در اینجاست که حدس می‌زنم آن‌ها به نحوی هر پلی‌گون را در کاشی در حال بارگذاری تعریف می‌کنند که چنین داده‌ای بر اساس پنجره ظرف نقشه تعریف می‌شود. جالب این است که هر ورودی یک مقدار «S» دارد که فرض می‌کنم به عنوان یک ویژگی اختیاری یا مقدار لینک ویژگی استفاده می‌شود و در انتهای هر ورودی ناحیه‌ای وجود دارد که به نظر می‌رسد یک شناسه ویژه هر بردار را به همراه شناسه لایه تعریف می‌کند که حدس می‌زنم برای پیوستن داده‌های هر درخواست کاشی json در حال فراخوانی استفاده می‌شود. همچنین فرض می‌کنم آن‌ها به نحوی راهی برای تعیین و تقسیم خودکار داده‌هایی که باید برای هر کاشی بارگذاری شود بسته به اندازه داده‌ای که باید برای کاشی درخواست‌شده بارگذاری شود پیدا کرده‌اند. در اینجا تجزیه استخراج‌شده از یکی از این درخواست‌ها آمده است: {"width":256,"height":256,"tile": {"pixels": [0,6461,-1,0,5,148,0,509,-1,10715,-1,1,-1,251,-1,1,-1,1,-1,251,-2,3,-1,255,-1,249,-2,5,-2,247,-1,509,-3,251,-1,2,-2,253,-2,252,-2,254,-1,255,-1,254,-1,255,-1,1276,-2,13,-1,233,-1,2,-1,253,-1,1,-1,255,-1,247,-1,1306,-1,1533,-1,1269,-1,1276,-1,2303,-1]}, "styles": [{"f":"rgb(99,230,101)","s":"rgb(5,148,0)","lw":"0"}], "geom": [ {"s":0,"p":[4,143,5,144,3,146,1,146,2,143,4,143],"c":"layer1156_5098"}, {"s":0,"p":[-2,143,0,140,2,141,2,144,1,146,-2,144,-2,143],"c":"layer1156_5067"}, {"s":0,"p":[7,143,5,144,4,143,2,143,2,141,5,138,6,139,5,141,7,143],"c":"layer1156_5051"}, {"s":0,"p":[10,141,11,137,12,137,14,137,12,142,9,143,9,142,10,141],"c":"layer1156_5041"}, {"s":0,"p":[1,136,0,140,-2,143,-2,136,1,136],"c":"layer1156_5038"}, {"s":0,"p":[8,143,5,141,5,137,8,136,10,137,10,141,8,143],"c":"layer1156_5033"}, {"s":0,"p":[5,137,2,141,0,140,1,136,1,136,2,135,3,136,5,137],"c":"layer1156_5028"}, {"s":0,"p":[10,134,12,136,11,138,8,135,10,134],"c":"layer1156_5020"}, {"s":0,"p":[-2,133,0,136,-2,136,-2,133],"c":"layer1156_5005"}, {...} ... ] } چگونه می‌توانیم همان (یا مشابه) سرعت را با استفاده از پست‌جی‌آی‌اس (که به نظر می‌رسد آن‌ها هم از آن استفاده می‌کنند) تکرار کنیم؟

پاسخ‌ها (2)

۱۵ بهمن ۱۴۰۴، ۰۸:۵۵
من قبلاً این تکنیک را در گذشته دیده‌ام. زین مِمون (Zain Memon) (از ترولیا) آن را برایم توضیح داد و زمانی که میچال میگورسکی (Michal Migurski) TileStache را ایجاد می‌کرد کمک کرد. زین در حالی که دموی ترولیای خودش را که از این تکنیک استفاده می‌کند در یکی از جلسات قدیمی‌تر SF GeoMeetup توضیح می‌داد، سراغ آن رفت. در واقع، اگر هفته آینده در SF هستید (این تلاش ناقص من برای معرفی است، او به این موضوع خواهد پرداخت، پس با خیال راحت شرکت کنید :)
بسیار خب، حالا سراغ توضیح. اول، وقتی به فایل‌های json بالا نگاه می‌کنید کمی در جای اشتباه نگاه می‌کنید. بگذارید (تا جایی که می‌توانم کوتاه) توضیح دهم که چرا. کاشی‌ها فقط به عنوان کاشی‌های رندر معمولی منتقل می‌شوند، چیز خاصی نیست، ما بلدیم چگونه این کار را انجام دهیم و بنابراین نیازی به توضیح آن ندارم. اگر آن را در فایرباگ بازرسی کنید، می‌بینید که تعداد زیادی تصویر هم دریافت می‌کنید که به نظر می‌رسد خالی هستند، مثل همین یکی. چرا خالی است؟ این طور نیست. پیکسل‌ها داده دارند - فقط داده تصویر قابل مشاهده سنتی نیستند. آن‌ها از یک تکنیک بسیار هوشمندانه برای انتقال داده‌هایی استفاده می‌کنند که در خود پیکسل‌ها رمزگذاری شده است. آنچه در دهه گذشته رخ داده، این است که مردم سهولت خواندن و قابل حمل بودن داده‌ها را به هزینه کارایی ذخیره‌سازی مبادله کرده‌اند. مثال زیر از داده‌های نمونه xml را در نظر بگیرید: <data> <feature> <point> <x> -32.1231 </x> <y> 10.31243 </y> </point> <type> sold </type> </feature> <feature> <point> <x> -33.1231 </x> <y> 11.31243 </y> </point> <type> available </type> </feature> </data> خب، چند بایت برای انتقال این داده؟ به شرطی که utf8 باشیم (1 بایت به ازای هر کاراکتر هنگام برخورد با این محتوا). خوب، ما حدود 176 کاراکتر داریم (بدون احتساب تب‌ها یا فاصله‌ها) که آن را 176 بایت می‌کند (این به دلایل مختلفی خوش‌بینانه است که برای سادگی از آن‌ها چشم‌پوشی می‌کنم). توجه داشته باشید، این فقط برای 2 نقطه است! با این حال، یک آدم مدعی که نمی‌فهمد درباره چه چیزی صحبت می‌کند، جایی، ادعا خواهد کرد که «json فشرده‌سازی بالاتری به شما می‌دهد». خوب، همان مزخرفات xml را به صورت json بگذاریم: { "data": [ "feature" : { "x" : -32.1231, "y" : 10.31243 , "type": "sold" }, "feature" : { "x" : -33.1231, "y" :11.31243, "type": "avail" }, ] } چند بایت اینجا؟ حدود 115 کاراکتر. حتی کمی تقلب کردم و کوچک‌ترش کردم. فرض کنید ناحیه من 256x256 پیکسل را پوشش می‌دهد و در سطح زومی آنقدر بالا هستم که هر ویژگی به صورت یک پیکسل رندر می‌شود و آنقدر ویژگی دارم که کاملاً پر است. چقدر داده برای نمایش آن 65,536 ویژگی نیاز دارم؟ 54 کاراکتر (یا بایت utf - و حتی چیزهای دیگری را نادیده می‌گیرم) به ازای هر ورودی «feature» ضرب در 65,536 = 3,538,944 یا حدود 3.3MB فکر می‌کنم متوجه شدید. اما این طوری است که ما در یک معماری سرویس‌گرا داده را منتقل می‌کنیم. محتوای قابل خواندنِ حجیم و بی‌مصرف. چه می‌شد اگر می‌خواستم همه چیز را در یک طرح باینری منتقل کنم که خودم اختراع کرده‌ام؟ فرض کنید به جای آن، آن اطلاعات را در یک تصویر تک‌باندی (یعنی سیاه و سفید) رمزگذاری کنم. و تصمیم بگیرم که 0 یعنی فروخته‌شده، 1 یعنی موجود، و 2 یعنی نمی‌دانم. جالب است، در 1 بایت، 256 گزینه دارم که می‌توانم استفاده کنم - و فقط از 2 یا 3 مورد آن‌ها در این مثال استفاده می‌کنم. هزینه ذخیره‌سازی آن چیست؟ 256x256x1 (فقط یک باند). 65,536 بایت یا 0.06MB. و این حتی سایر تکنیک‌های فشرده‌سازی را هم در نظر نمی‌گیرد که از چند دهه تحقیق در فشرده‌سازی تصویر به صورت رایگان به دست می‌آورم. در این مرحله، باید از خود بپرسید چرا مردم به سادگی داده‌ها را به جای سریال‌سازی به json، در قالب باینری ارسال نمی‌کنند؟ خوب، اول، معلوم می‌شود که جاوااسکریپت در انتقال داده‌های باینری خیلی ضعیف است، به همین دلیل مردم از نظر تاریخی این کار را انجام نداده‌اند. یک راه‌حل کاری فوق‌العاده توسط برخی افراد استفاده شده است وقتی ویژگی‌های جدید HTML5 منتشر شد، به ویژه کانواس. پس این راه‌حل کاری فوق‌العاده چیست؟ معلوم می‌شود، می‌توانید داده‌ها را روی سیم رمزگذاری‌شده در چیزی که به نظر می‌رسد یک تصویر است ارسال کنید، سپس آن تصویر را داخل یک Canvas (کانواس) HTML5 بگذارید، که به شما امکان می‌دهد مستقیماً پیکسل‌ها را دستکاری کنید! حالا راهی برای گرفتن آن داده، رمزگشایی آن در سمت کلاینت، و تولید آبجکت‌های json در کلاینت دارید. یک لحظه بایستید و به این فکر کنید. شما راهی برای رمزگذاری حجم عظیمی از داده‌های معنادار ژئورفرنس‌شده در یک قالب بسیار فشرده دارید، چندین مرتبه قدر کوچک‌تر از هر کاری که به طور سنتی در برنامه‌های وب انجام می‌شود، و آن‌ها را در جاوااسکریپت دستکاری کنید. حتی نیازی نیست از کانواس HTML برای رسم استفاده شود، فقط به عنوان یک مکانیزم رمزگشایی باینری استفاده می‌شود! این است آنچه آن همه تصویری که در فایرباگ می‌بینید درباره آن است. یک تصویر، با داده‌های رمزگذاری‌شده برای هر کاشی‌ای که دانلود می‌شود. آن‌ها فوق‌العاده کوچک هستند، اما داده معنادار دارند. پس چگونه این‌ها را در سمت سرور رمزگذاری می‌کنید؟ خوب، باید داده‌ها را در سمت سرور تعمیم دهید و برای هر سطح زومی که داده رمزگذاری‌شده دارد یک کاشی معنادار ایجاد کنید. در حال حاضر، برای انجام این کار، باید خودتان آن را بسازید - یک راه‌حل متن‌باز آماده وجود ندارد، اما همه ابزارهای لازم برای این کار را در اختیار دارید. PostGIS (پست‌جی‌آی‌اس) تعمیم را از طریق GEOS انجام می‌دهد، TileCache می‌تواند برای کش کردن و کمک به راه‌اندازی تولید کاشی‌ها استفاده شود. در سمت کلاینت، باید از HTML5 Canvas برای انتقال «کاشی‌های جعلی» ویژه استفاده کنید و سپس می‌توانید از OpenLayers برای ایجاد آبجکت‌های جاوااسکریپت واقعی سمت کلاینت استفاده کنید که بردارها را با افکت‌های هاور نشان می‌دهند. اگر نیاز به رمزگذاری داده بیشتری دارید، به خاطر داشته باشید که همیشه می‌توانید برای هر پیکسل تصاویر RGBA تولید کنید (که 4 بایت به ازای هر پیکسل یا 4,294,967,296 عددی که می‌توانید در هر پیکسل نمایش دهید به شما می‌دهد). می‌توانم چندین راه برای استفاده از آن به ذهنم برسانم :) به‌روزرسانی: پاسخ به سوال QGIS در زیر. QGIS مانند بیشتر GISهای رومیزی، مجموعه ثابتی از سطوح زوم ندارد. آن‌ها انعطاف زوم کردن در هر مقیاسی و فقط رندر کردن را دارند. آیا می‌توانند داده‌ها را از منابع مبتنی بر WMS یا کاشی نشان دهند؟ البته می‌توانند، اما بیشتر اوقات در این کار واقعاً ساده‌لوح هستند: زوم به یک محدوده دیگر، محاسبه جعبه محدود، محاسبه کاشی‌های مورد نیاز، گرفتن آن‌ها، نمایش آن‌ها. بیشتر اوقات چیزهای دیگر مانند کش‌های هدر http را نادیده می‌گیرند که باعث می‌شد مجبور به واکشی مجدد نباشند. گاهی اوقات یک مکانیزم کش ساده پیاده‌سازی می‌کنند (کاشی را ذخیره کن، اگر خواستید بررسی کن، درخواستش نکن). اما این کافی نیست. با این تکنیک، کاشی‌ها و بردارها باید در هر سطح زوم دوباره واکشی شوند. چرا؟ زیرا بردارها برای تطبیق با سطوح زوم تعمیم داده شده‌اند. در مورد کل حقه قرار دادن کاشی‌ها روی کانواس HTML5 تا بتوانید به بافرها دسترسی پیدا کنید، کل این کار ضروری نیست. QGIS به شما امکان می‌دهد کد در پایتون و C++ بنویسید، هر دو زبان پشتیبانی عالی برای مدیریت بافرهای باینری دارند، بنابراین این راه‌حل برای این پلتفرم واقعاً بی‌ربط است. *به‌روزرسانی 2*: سوالی درباره نحوه ایجاد کاشی‌های برداری تعمیم‌یافته در وهله اول وجود داشت (گام کوچک 1 قبل از اینکه بتوانید نتایج را در تصاویر سریال کنید). شاید به اندازه کافی روشن نکرده‌ام. Tilestache به شما امکان می‌دهد «کاشی‌های برداری» مؤثری از داده‌های خود در هر سطح زوم ایجاد کنید (حتی گزینه‌ای دارد که به شما امکان می‌دهد یا برش بزنید یا نزنید وقتی داده از مرز کاشی عبور می‌کند). این کار جدا کردن بردارها به کاشی‌ها در سطوح زوم مختلف را انجام می‌دهد. من گزینه «عدم برش» را انتخاب می‌کنم (اما یک کاشی دلخواه را انتخاب می‌کند که ناحیه بیشتری را پوشش می‌دهد). سپس می‌توانید هر بردار را از گزینه تعمیم GEOS با یک عدد بزرگ عبور دهید، در واقع، می‌خواهید آنقدر بزرگ باشد که خطوط چندگانه و پلی‌گون‌ها روی خودشان جمع شوند، زیرا اگر این کار را بکنند، می‌توانید آن‌ها را از آن سطح زوم حذف کنید چون برای آن مرحله بی‌ربط هستند. Tilestache حتی به شما امکان می‌دهد فراهم‌کننده‌های داده پایتونی آسان بنویسید که می‌توانید این منطق را در آن‌ها قرار دهید. در آن مرحله، می‌توانید انتخاب کنید آن‌ها را به عنوان فایل‌های json ارائه کنید (همان‌طور که با برخی از نمونه‌های نقشه آفریقا انجام می‌دهند) یا به عنوان هندسه‌های سریال‌شده در pngها، همان‌طور که در نمونه‌های دیگر (یا نمونه ترولیا) که در بالا آوردم انجام می‌دهند.