أدوات استشعار الويب

استخدِم واجهة برمجة تطبيقات الاستشعار العامة للوصول إلى أدوات الاستشعار على الجهاز، مثل مقاييس التسارع والجيروسكوب ومقاييس المغناطيسية.

واليوم، يتم استخدام بيانات أجهزة الاستشعار في العديد من التطبيقات الخاصة بالأنظمة الأساسية لإتاحة حالات الاستخدام، مثل الألعاب الغامرة وتتبُّع اللياقة البدنية والواقع المعزّز أو الافتراضي. ألن يكون من الرائع سد الفجوة بين تطبيقات النظام الأساسي وتطبيقات الويب؟ أدخِل واجهة برمجة تطبيقات أداة الاستشعار العامة للويب.

ما هي واجهة برمجة تطبيقات أداة الاستشعار العامة؟

واجهة برمجة التطبيقات General Sensor API هي مجموعة من الواجهات التي تعرض أجهزة الاستشعار على النظام الأساسي للويب. تتكوّن واجهة برمجة التطبيقات من واجهة Sensor الأساسية ومجموعة من فئات أجهزة الاستشعار الملموسة التي تم تصميمها أعلاه. يؤدي وجود واجهة أساسية إلى تبسيط عملية التنفيذ وتحديد المواصفات لفئات المستشعرات الملموسة. على سبيل المثال، ألقِ نظرة على صف Gyroscope. إنه صغير جدًا! يتم تحديد الوظيفة الأساسية من خلال الواجهة الأساسية، ويتم توسيع نطاق السمة Gyroscope من خلال ثلاث سمات تمثّل السرعة الزاويّة.

وتتفاعل بعض فئات أجهزة الاستشعار مع المستشعرات الفعلية للأجهزة، مثل فئات مقياس التسارع أو الجيروسكوب. ويُشار إلى هذه الأدوات باسم أدوات استشعار منخفضة المستوى. تدمج أدوات الاستشعار الأخرى، التي يُشار إليها باسم أجهزة استشعار الاندماج، البيانات من عدة أدوات استشعار منخفضة المستوى لعرض المعلومات التي قد يحتاج النص البرمجي إلى حسابها بها. على سبيل المثال، يوفّر أداة الاستشعار AbsoluteOrientation مصفوفة دوران جاهزة للاستخدام رباعية وأربع في أربع مراحل استنادًا إلى البيانات التي تم الحصول عليها من مقياس التسارع والجيروسكوب ومقياس المغناطيسية.

قد تعتقد أن منصة الويب توفر بالفعل بيانات الاستشعار وأنت على حق تمامًا! على سبيل المثال، تعرض أحداث DeviceMotion وDeviceOrientation بيانات جهاز استشعار الحركة. إذًا لماذا نحتاج إلى واجهة برمجة تطبيقات جديدة؟

مقارنةً بالواجهات الحالية، توفر واجهة برمجة التطبيقات Generic Sensor API عددًا كبيرًا من المزايا:

  • واجهة برمجة تطبيقات أداة الاستشعار العامة هي إطار عمل لأداة استشعار يمكن توسيعه بسهولة باستخدام فئات أدوات الاستشعار الجديدة وستحتفظ كل فئة من هذه الفئات بالواجهة العامة. يمكن إعادة استخدام رمز العميل المكتوب لأحد أنواع المستشعرات لنوع آخر مع إجراء تعديلات قليلة جدًا!
  • يمكنك ضبط إعدادات أداة الاستشعار. على سبيل المثال، يمكنك ضبط معدّل أخذ العيّنات المناسب لاحتياجات تطبيقك.
  • يمكنك اكتشاف ما إذا كان المستشعر متوفرًا على النظام الأساسي أم لا.
  • تشتمل قراءات أدوات الاستشعار على طوابع زمنية عالية الدقة، ما يتيح مزامنة أفضل مع الأنشطة الأخرى في تطبيقك.
  • يتم تحديد نماذج بيانات أدوات الاستشعار وأنظمة الإحداثيات بوضوح، ما يسمح لمورّدي المتصفحات بتنفيذ حلول قابلة للتشغيل التفاعلي.
  • إنّ الواجهات المستندة إلى أداة استشعار عامة غير مرتبطة بنموذج العناصر في المستند (DOM) (ما يعني أنّها ليست navigator أو window)، وهذا يتيح فرصًا مستقبلية لاستخدام واجهة برمجة التطبيقات ضمن مشغّلي الخدمات أو تنفيذها في أوقات تشغيل JavaScript بلا واجهة مستخدم رسومية، مثل الأجهزة المضمَّنة.
  • تُعدّ جوانب الأمان والخصوصية على رأس الأولويات في واجهة برمجة تطبيقات أداة الاستشعار العامة، وتوفّر أمانًا أفضل بكثير مقارنةً بواجهات برمجة تطبيقات أدوات الاستشعار القديمة. هناك دمج مع Permissions API.
  • تتوفر المزامنة التلقائية مع إحداثيات الشاشة لـ Accelerometer وGyroscope وLinearAccelerationSensor وAbsoluteOrientationSensor وRelativeOrientationSensor وMagnetometer.

واجهات برمجة التطبيقات لأجهزة الاستشعار العامة المتاحة

في وقت كتابة هذا التقرير، هناك العديد من أجهزة الاستشعار التي يمكنك تجربتها.

أجهزة استشعار الحركة:

  • Accelerometer
  • Gyroscope
  • LinearAccelerationSensor
  • AbsoluteOrientationSensor
  • RelativeOrientationSensor
  • GravitySensor

أجهزة الاستشعار البيئية:

  • AmbientLightSensor (خلف علامة #enable-generic-sensor-extra-classes في Chromium)
  • Magnetometer (خلف علامة #enable-generic-sensor-extra-classes في Chromium)

رصد الميزات

يعتبر اكتشاف ميزات واجهات برمجة تطبيقات الأجهزة أمرًا صعبًا، نظرًا لأنك تحتاج إلى اكتشاف ما إذا كان المتصفح يتوافق مع الواجهة المعنية، وما إذا كان الجهاز يحتوي على أداة الاستشعار المقابلة لها. التحقق مما إذا كان المتصفّح يتيح استخدام إحدى الواجهات أم لا (استبدِل Accelerometer بأي من الواجهات الأخرى المذكورة أعلاه).

if ('Accelerometer' in window) {
  // The `Accelerometer` interface is supported by the browser.
  // Does the device have an accelerometer, though?
}

للحصول على نتيجة مفيدة لرصد الميزات، عليك محاولة الاتصال بجهاز الاستشعار أيضًا. يوضح هذا المثال كيفية القيام بذلك.

let accelerometer = null;
try {
  accelerometer = new Accelerometer({ frequency: 10 });
  accelerometer.onerror = (event) => {
    // Handle runtime errors.
    if (event.error.name === 'NotAllowedError') {
      console.log('Permission to access sensor was denied.');
    } else if (event.error.name === 'NotReadableError') {
      console.log('Cannot connect to the sensor.');
    }
  };
  accelerometer.onreading = (e) => {
    console.log(e);
  };
  accelerometer.start();
} catch (error) {
  // Handle construction errors.
  if (error.name === 'SecurityError') {
    console.log('Sensor construction was blocked by the Permissions Policy.');
  } else if (error.name === 'ReferenceError') {
    console.log('Sensor is not supported by the User Agent.');
  } else {
    throw error;
  }
}

الملء التلقائي

بالنسبة إلى المتصفحات التي لا تتيح واجهة برمجة التطبيقات General Sensor API، يتوفّر رمز polyfill. يتيح لك رمز polyfill تحميل عمليات أدوات الاستشعار ذات الصلة فقط.

// Import the objects you need.
import { Gyroscope, AbsoluteOrientationSensor } from './src/motion-sensors.js';

// And they're ready for use!
const gyroscope = new Gyroscope({ frequency: 15 });
const orientation = new AbsoluteOrientationSensor({ frequency: 60 });

ما هي كل أدوات الاستشعار هذه؟ كيف يمكنني استخدامها؟

أدوات الاستشعار هي مجال قد يحتاج إلى مقدمة موجزة. إذا كنت على دراية بأجهزة الاستشعار، يمكنك الانتقال مباشرةً إلى قسم الترميز العملي. بخلاف ذلك، لنلقِ نظرة على كل جهاز استشعار متوافق بالتفصيل.

مقياس التسارع وجهاز استشعار التسارع الخطي

قياسات أداة استشعار مقياس التسارع

يقيس جهاز الاستشعار Accelerometer تسارع جهاز يستضيف أداة الاستشعار على ثلاثة محاور (X وY وZ). هذا المستشعر هو أداة استشعار شمولية، ما يعني أنّه عندما يكون الجهاز في وضع السقوط الحر الخطي، سيكون إجمالي العجلة المقاس 0 م/ثانية2، وعندما يكون الجهاز مسطحًا على الطاولة، سيكون التسارع في الاتجاه الأعلى (المحور Z) مساويًا لجاذبية الأرض، أي أنّ g ≈ م/قوّة 128 متر لدفع الجهاز للجدول الزمني أعلى. إذا دفعت الجهاز إلى اليمين، فسيكون التسارع على المحور س موجبًا أو سالبًا إذا تم تسريع الجهاز من اليمين باتجاه اليسار.

يمكن استخدام مقاييس التسارع لعدّة خطوات، مثل عدّ الخطوات أو استشعار الحركة أو الاتّجاه البسيط للأجهزة. في كثير من الأحيان، يتم دمج قياسات مقياس التسارع مع البيانات الواردة من مصادر أخرى لإنشاء أجهزة استشعار الاندماج، مثل مستشعرات الاتجاه.

يقيس LinearAccelerationSensor التسارع المطبَّق على الجهاز الذي يستضيف أداة الاستشعار، باستثناء مساهمة الجاذبية. عندما يكون الجهاز في وضع عدم النشاط، على سبيل المثال يكون مسطحًا على الطاولة، يمكن أن يقيس جهاز الاستشعار ≈ 0 م/ث2 التسارع على ثلاثة محاور.

أداة استشعار الجاذبية

ومن الممكن أن يستخلص المستخدمون يدويًا قراءات قريبة من تلك الخاصة بجهاز استشعار الجاذبية عن طريق الفحص اليدوي لقراءات Accelerometer وLinearAccelerometer، إلا أن هذا قد يكون مرهقًا ويعتمد على دقة القيم التي تقدمها أدوات الاستشعار هذه. يمكن للأنظمة الأساسية مثل Android توفير قراءات الجاذبية كجزء من نظام التشغيل، ومن المفترض أن تكون أرخص من حيث الحوسبة، وتوفر قيمًا أكثر دقة اعتمادًا على جهاز المستخدم، وأن تكون أسهل في الاستخدام من حيث بيئة برمجة التطبيقات. تعرض دالة GravitySensor تأثير التسارع على طول المحور "س" و"ص" و"ع" للجهاز بسبب الجاذبية.

الجيروسكوب

قياسات أداة استشعار الجيروسكوب

يقيس جهاز الاستشعار Gyroscope السرعة الزاويّة بوحدات الراديان في الثانية حول المحور س المحلي وص وع. تحتوي معظم أجهزة المستهلك على أجهزة جيروسكوبية ميكانيكية (MEMS)، وهي أدوات استشعار جوفية تقيس معدّل الدوران استنادًا إلى قوة كوريوليس الجوهرية. تكون الجيروسكوب MEMS عرضة للانجراف الناجم عن حساسية الجاذبية للمستشعر التي تُشوّه النظام الميكانيكي الداخلي للمستشعر. تتذبذب الجيروسكوب في ترددات عالية نسبية، على سبيل المثال: 10 ثوانٍ من كيلوهرتز، وبالتالي، قد تستهلك مزيدًا من الطاقة مقارنةً بأجهزة الاستشعار الأخرى.

أدوات استشعار الاتجاه

قياسات أداة استشعار الاتجاه المطلق

جهاز AbsoluteOrientationSensor هو مستشعر اندماج يقيس دوران جهاز في ما يتعلق بنظام إحداثيات الأرض، في حين توفّر RelativeOrientationSensor بيانات تمثل دوران جهاز يستضيف أجهزة استشعار الحركة في ما يتعلق بنظام الإحداثيات المرجعية الثابتة.

تتوافق جميع إطارات عمل JavaScript الثلاثية الأبعاد الحديثة مع الجداول الرباعية ومصفوفات التدوير لتمثيل الدوران. ومع ذلك، إذا كنت تستخدم WebGL مباشرةً، توفّر OrientationSensor بسهولة السمة quaternion وطريقة populateMatrix(). في ما يلي بعض المقتطفات:

three.js

let torusGeometry = new THREE.TorusGeometry(7, 1.6, 4, 3, 6.3);
let material = new THREE.MeshBasicMaterial({ color: 0x0071c5 });
let torus = new THREE.Mesh(torusGeometry, material);
scene.add(torus);

// Update mesh rotation using quaternion.
const sensorAbs = new AbsoluteOrientationSensor();
sensorAbs.onreading = () => torus.quaternion.fromArray(sensorAbs.quaternion);
sensorAbs.start();

// Update mesh rotation using rotation matrix.
const sensorRel = new RelativeOrientationSensor();
let rotationMatrix = new Float32Array(16);
sensor_rel.onreading = () => {
  sensorRel.populateMatrix(rotationMatrix);
  torus.matrix.fromArray(rotationMatrix);
};
sensorRel.start();

تستخدم صاحبة قناة BABYLON

const mesh = new BABYLON.Mesh.CreateCylinder('mesh', 0.9, 0.3, 0.6, 9, 1, scene);
const sensorRel = new RelativeOrientationSensor({ frequency: 30 });
sensorRel.onreading = () => mesh.rotationQuaternion.FromArray(sensorRel.quaternion);
sensorRel.start();

WebGL

// Initialize sensor and update model matrix when new reading is available.
let modMatrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
const sensorAbs = new AbsoluteOrientationSensor({ frequency: 60 });
sensorAbs.onreading = () => sensorAbs.populateMatrix(modMatrix);
sensorAbs.start();

// Somewhere in rendering code, update vertex shader attribute for the model
gl.uniformMatrix4fv(modMatrixAttr, false, modMatrix);

تتيح أدوات استشعار الاتجاه حالات استخدام مختلفة، مثل تشغيل الألعاب الغامرة والواقع المعزّز والواقع الافتراضي.

للحصول على مزيد من المعلومات حول أجهزة استشعار الحركة وحالات الاستخدام المتقدّمة والمتطلبات، يمكنك الاطّلاع على المستند الشرح حول أجهزة استشعار الحركة.

المزامنة مع إحداثيات الشاشة

يتم تلقائيًا التعامل مع قراءات أدوات الاستشعار المكانية في نظام إحداثي محلي مرتبط بالجهاز ولا يأخذ اتجاه الشاشة في الاعتبار.

نظام إحداثيات الجهاز
نظام تنسيق الجهاز

مع ذلك، تتطلب العديد من حالات الاستخدام، مثل الألعاب أو الواقع المعزز والواقع الافتراضي، تحليل قراءات المستشعر في نظام إحداثي مرتبط باتجاه الشاشة.

نظام إحداثيات الشاشة
نظام تنسيق الشاشة

في السابق، كان يجب تنفيذ إعادة تخصيص قراءات المستشعر لإحداثيات الشاشة باستخدام JavaScript. هذه الطريقة غير فعّالة، كما أنّها تزيد بشكل ملحوظ من تعقيد رمز تطبيق الويب، إذ يجب أن يراقب تطبيق الويب التغييرات في اتجاه الشاشة ويُجري عمليات تحويل الإحداثيات لقراءات المستشعر، وهذا ليس بالأمر الهين بالنسبة إلى زوايا أويلر أو الرسم الرباعي.

تُوفّر واجهة برمجة تطبيقات الاستشعار العامة حلاً أبسط وموثوقًا به. ويمكن ضبط نظام الإحداثيات المحلي لجميع فئات أدوات الاستشعار المكانية المحددة: Accelerometer وGyroscope وLinearAccelerationSensor وAbsoluteOrientationSensor وRelativeOrientationSensor وMagnetometer. من خلال تمرير الخيار referenceFrame إلى الدالة الإنشائية لكائن أداة الاستشعار، يحدد المستخدم ما إذا كان سيتم التعامل مع القراءات المعروضة في إحداثيات الجهاز أو الشاشة.

// Sensor readings are resolved in the Device coordinate system by default.
// Alternatively, could be RelativeOrientationSensor({referenceFrame: "device"}).
const sensorRelDevice = new RelativeOrientationSensor();

// Sensor readings are resolved in the Screen coordinate system. No manual remapping is required!
const sensorRelScreen = new RelativeOrientationSensor({ referenceFrame: 'screen' });

لنرمز إلى الترميز!

واجهة برمجة تطبيقات أداة الاستشعار العامة بسيطة وسهلة الاستخدام. تتضمّن واجهة أداة الاستشعار طريقتَين start() وstop() للتحكّم في حالة أداة الاستشعار، بالإضافة إلى عدّة معالِجات للأحداث لتلقّي الإشعارات حول تفعيل أداة الاستشعار والأخطاء والقراءات المتاحة حديثًا. عادةً ما تضيف فئات أدوات الاستشعار الملموسة سمات القراءة الخاصة بها إلى الفئة الأساسية.

بيئة تطوير

خلال عملية التطوير، سيكون بإمكانك استخدام أدوات الاستشعار من خلال localhost. وإذا كنت تطوِّر الأجهزة الجوّالة، اضبط إعدادات إعادة توجيه المنفذ للخادم المحلي.

عندما يصبح الرمز جاهزًا، يمكنك نشره على خادم يتيح بروتوكول HTTPS. يتم عرض صفحات GitHub عبر HTTPS، مما يجعلها مكانًا رائعًا لمشاركة عروضك التوضيحية.

تدوير النموذج الثلاثي الأبعاد

في هذا المثال البسيط، نستخدم البيانات من مستشعر الاتجاه المطلق لتعديل الربع حول الدوران لنموذج ثلاثي الأبعاد. إنّ السمة model هي مثيل من فئة الثلاثة.js Object3D ويحتوي على السمة quaternion. يوضح مقتطف الرمز التالي من العرض التوضيحي لهاتف الاتجاه كيفية استخدام أداة استشعار الاتجاه المطلق لتدوير نموذج ثلاثي الأبعاد.

function initSensor() {
  sensor = new AbsoluteOrientationSensor({ frequency: 60 });
  sensor.onreading = () => model.quaternion.fromArray(sensor.quaternion);
  sensor.onerror = (event) => {
    if (event.error.name == 'NotReadableError') {
      console.log('Sensor is not available.');
    }
  };
  sensor.start();
}

سيظهر اتجاه الجهاز في دوران model ثلاثي الأبعاد في مشهد WebGL.

يعدِّل جهاز الاستشعار اتجاه التصميم الثلاثي الأبعاد.
يعدّل جهاز الاستشعار اتجاه النموذج الثلاثي الأبعاد.

جهاز قياس الثقوب

يتم استخراج مقتطف الرمز التالي من العرض التوضيحي لأداة punchmeter الذي يوضّح طريقة استخدام أداة استشعار التسارع الخطي لحساب أقصى سرعة لأي جهاز على افتراض أنه ثابت في البداية.

this.maxSpeed = 0;
this.vx = 0;
this.ax = 0;
this.t = 0;

/* … */

this.accel.onreading = () => {
  let dt = (this.accel.timestamp - this.t) * 0.001; // In seconds.
  this.vx += ((this.accel.x + this.ax) / 2) * dt;

  let speed = Math.abs(this.vx);

  if (this.maxSpeed < speed) {
    this.maxSpeed = speed;
  }

  this.t = this.accel.timestamp;
  this.ax = this.accel.x;
};

يتم حساب السرعة الحالية على أنها تقريبية لتكامل دالة التسارع.

عرض توضيحي لتطبيق ويب لقياس سرعة الثقب
قياس سرعة اللكم

تصحيح الأخطاء وتجاوز أدوات الاستشعار باستخدام "أدوات مطوري البرامج في Chrome"

في بعض الحالات، لا تحتاج إلى جهاز فعلي لتشغيل واجهة برمجة تطبيقات الاستشعار العامة. وتوفِّر "أدوات مطوري البرامج في Chrome" دعمًا رائعًا لمحاكاة اتجاه الجهاز.

&quot;أدوات مطوري البرامج في Chrome&quot; المستخدَمة لإلغاء بيانات الاتجاه المخصّص لهاتف افتراضي
محاكاة اتجاه الجهاز باستخدام "أدوات مطوري البرامج في Chrome"

الخصوصية والأمان

وقراءات أدوات الاستشعار هي بيانات حساسة يمكن أن تتعرض لهجمات مختلفة من صفحات الويب الضارة. يؤدي تنفيذ واجهات برمجة تطبيقات أدوات الاستشعار العامة إلى فرض بعض القيود للتخفيف من المخاطر المحتملة للأمان والخصوصية. ويجب أن يضع المطوّرون الذين يريدون استخدام واجهة برمجة التطبيقات هذه القيود في الاعتبار، لذا سنذكرها بإيجاز.

HTTPS فقط

بما أنّ واجهة برمجة تطبيقات أداة الاستشعار العامة هي ميزة فعّالة، لا يسمح المتصفّح باستخدامها إلا في السياقات الآمنة. من الناحية العملية، يعني هذا أنّه لاستخدام واجهة برمجة تطبيقات أدوات الاستشعار العامة، عليك الوصول إلى صفحتك من خلال بروتوكول HTTPS. أثناء التطوير، يمكنك إجراء ذلك عبر http://localhost ولكن في مرحلة الإنتاج، ستحتاج إلى توفُّر HTTPS على خادمك. يمكنك الاطّلاع على مجموعة الأمان والسلامة للتعرّف على أفضل الممارسات والإرشادات.

دمج سياسة الأذونات

يتحكّم دمج سياسة الأذونات في واجهة برمجة تطبيقات Sensor API في الوصول إلى بيانات أجهزة الاستشعار لإطار معيّن.

وفقًا للإعدادات التلقائية، لا يمكن إنشاء كائنات Sensor إلا داخل الإطار الرئيسي أو الإطارات الفرعية من المصدر نفسه، وبالتالي منع إطارات iframe المتعددة المصادر من قراءة بيانات أداة الاستشعار بدون إذن. ويمكن تعديل هذا السلوك التلقائي من خلال تفعيل أو إيقاف الميزات المقابلة التي يتم التحكّم فيها من خلال السياسة بشكل صريح.

يوضّح المقتطف أدناه عملية منح إذن الوصول إلى بيانات مقياس التسارع إلى إطار iframe من مصادر متعددة، ما يعني أنّه يمكن الآن إنشاء كائنات Accelerometer أو LinearAccelerationSensor هناك.

<iframe src="https://third-party.com" allow="accelerometer" />

يمكن تعليق إرسال قراءات أجهزة الاستشعار

لا يمكن الوصول إلى قراءات أدوات الاستشعار إلا من خلال صفحة ويب مرئية، أي عندما يتفاعل المستخدم معها فعليًا. علاوة على ذلك، لن يتم تقديم بيانات أداة الاستشعار إلى الإطار الأصلي إذا تغيّر تركيز المستخدم إلى إطار فرعي من مصادر متعددة. ويمنع هذا الإطار الرئيسي استنتاج إدخال المستخدم.

ما هي الخطوات التالية؟

سنطرح في المستقبل القريب مجموعة من فئات أجهزة الاستشعار المحدّدة، مثل أداة استشعار الضوء المحيط أو أداة استشعار التقارب، إلا أنّه بفضل قابلية التوسّع الكبيرة التي يتميّز بها إطار عمل أجهزة الاستشعار العامة، يمكننا توقُّع ظهور المزيد من الفئات الجديدة التي تمثّل أنواعًا مختلفة من أجهزة الاستشعار.

هناك مجال آخر مهم للعمل المستقبلي هو تحسين واجهة برمجة تطبيقات أداة الاستشعار العامة نفسها، وتمثّل مواصفات أداة الاستشعار العامة حاليًا اقتراحًا للمرشحين، ما يعني أنّه لا يزال هناك وقت لإجراء إصلاحات وتوفير وظائف جديدة يحتاجها المطوّرون.

يمكنك المساعدة!

بلغت مواصفات أداة الاستشعار مستوى النضج مقترَح لاقتراح المرشح، لذلك نرحّب بملاحظات مطوّري برامج الويب والمتصفّحات. يُرجى إعلامنا بالميزات التي سيكون من الرائع إضافتها أو ما إذا أردت تعديل واجهة برمجة التطبيقات الحالية.

يُرجى عدم التردد في الإبلاغ عن مشاكل المواصفات إضافةً إلى bugs المتعلقة بتنفيذ Chrome.

المراجِع

شكر وتقدير

راجع هذه المقالة جو ميدلي وكايس باسك. صورة رئيسية من تصوير Misko في ويكيميديا كومنز