This project aims to develop a database to store environmental radioactivity measurements. It's shared in 2 parts :
- backend : the postgresql backend database
- api : a JSON Rest-like API developed in node.js to request the database
Source code is under licence Apache 2.0 (cf https://www.openradiation.org/fr/conditions-dutilisation#licence_apache2)
First at all, to use the API you need a key. Please ask by sending an email to [email protected] For only a few tests, you're allowed to use the following key : bde8ebc61cb089b8cc997dd7a0d0a434
By using this API, you accept the terms of use : https://www.openradiation.org/conditions-dutilisation
This project is self sufficient, but it is designed to work with the openradiation.org website. There is restricted access for this website to give informations about users and qualifications. The API is designed to be installed in two parts : the submit api and the request api.
API Name | Type | Available for request API | Available for submit API | Description |
---|---|---|---|---|
apparatusId | String | Unique sensor identifier | ||
apparatusVersion | String | Sensor firmware version | ||
apparatusSensorType | String | Sensor type : geiger, photodiode | ||
apparatusTubeType | String | Tube identification (only if apparatusSensorType = geiger) | ||
temperature | Integer | Temperature (°C) | ||
value | Real | * | Mandatory | Dose rate (µSv/h) |
hitsNumber | Integer | Counts measured during the whole measurement period. cps (counts per second) used to evaluate dose rate with the calibration function will be : cps = hitsNumber / (endTime - startTime) | ||
calibrationFunction | String | Calibration function used to calculate µSv/h from cps (counts per second). Format should be inline with these symbols : cps 0-9. -+/*^() max,. Example : 7.543*(cps-0.02)^2+0.001*(cps-0.02) | ||
startTime | Timestamp | * | Mandatory | Date of the beginning of the measurement (ISO GMT) |
endTime | Timestamp | Date of the end of the measurement (ISO GMT) | ||
latitude | Real | * | Mandatory | latitude |
longitude | Real | * | Mandatory | longitude |
accuracy | Real | Position accuracy in meters | ||
altitude | Integer | Altitude above sea in meters | ||
altitudeAccuracy | Real | Altitude accuracy in meters | ||
endLatitude | Real | latitude at the end of the measurement | ||
endLongitude | Real | longitude at the end of the measurement | ||
endAccuracy | Real | Position accuracy in meters at the end of the measurement | ||
endAltitude | Integer | Altitude above sea in meters at the end of the measurement | ||
endAltitudeAccuracy | Real | Altitude accuracy in meters at the end of the measurement | ||
deviceUuid | String | Smartphone device UUID (see http://plugins.cordova.io/#/package/org.apache.cordova.device) | ||
devicePlatform | String | Smartphone device platform | ||
deviceVersion | String | Smartphone device OS version | ||
deviceModel | String | Smartphone device model | ||
reportUuid | String | * | Mandatory | Unique measurement UUID (UUIDv4 format) client-side generated |
manualReporting | Boolean | Manual Reporting : true, false (default:true). False if the data is not entered by a human being | ||
organisationReporting | String | Software version (sample:openradiation_v1) | ||
reportContext | String | Never | Report context : emergency, routine, exercise, test (default:test). test:data are not registrated but you can test api use, emergency and exercise:not used,routine:you should use this one ! | |
description | String | Free description (only if userId is specified) | ||
measurementHeight | Integer | Measurement height above the ground (in meters). It is measurement height from the ground (and not altitude above sea level). | ||
tags | Json array of string | Free tags list [tag1 ; tag2] (only if userId is specified) | ||
enclosedObject | String | Base64 encoded Image. The size shoudn't exceeded 1mb and format should be closed from 600*800 pixels (width * height). The value should be a data URI scheme 'data:image/;base64,'. (only if userId is specified) | ||
userId | String | Openradiation.org user id | ||
userPwd | String | Never | Openradiation.org plain text password (mandatory if userId is specified) | |
measurementEnvironment | String | Measurement environment : countryside, city, ontheroad, inside, plane (if plane, qualification is set to plane) | ||
rain | Boolean | Rain : true if it rains during the measurement | ||
flightNumber | String | if measurementEnvironment is plane, flightNumber of the commercial flight in capital letters (AITA code followed by number, example: AF179) | ||
seatNumber | String | if measurementEnvironment is plane, seatNumber in capital letters with row number first (example: 14C) | ||
windowSeat | Boolean | if measurementEnvironment is plane, windowSeat : true if the seat where is the sensor is next to the window | ||
storm | Boolean | Storm : true if storm crossing during the measurement | ||
flightId | Integer | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, measurement is associated to a flightId (flightId permits to retrieve all measurements in a same flight) | |
refinedLatitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, latitude is refined with the track of the plane | |
refinedLongitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, longitude is refined with the track of the plane | |
refinedAltitude | Integer | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, altitude is refined with the track of the plane | |
refinedEndLatitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, endLatitude is refined with the track of the plane | |
refinedEndLongitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, endLongitude is refined with the track of the plane | |
refinedEndAltitude | Integer | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, endAltitude is refined with the track of the plane | |
departureTime | Timestamp | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, actual departure time of the plane | |
arrivalTime | Timestamp | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, actual arrival time of the plane | |
airportOrigin | String | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, airport origin (AITA) of the plane | |
airportDestination | String | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, airport destination (AITA) of the plane | |
aircraftType | String | No, determinated by the API | if measurementEnvironment is plane and flightNumber recognize, aircraft type of the plane | |
firstLatitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, latitude of the first measurement or latitude of the origin airport | |
firstLongitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, longitude of the first measurement or longitude of the origin airport | |
midLatitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, latitude picked up from a measurement or in the middle of the plane track | |
midLongitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, longitude picked up from a measurement or in the middle of the plane track | |
lastLatitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, latitude of the last measurement or latitude of the destination airport | |
lastLongitude | Real | No, determinated by the API | if measurementEnvironment is plane and flightNumber setted, longitude of the last measurement or longitude of the destination airport | |
dateAndTimeOfCreation | Timestamp | No, but always determinated by the API | Date of registration in the database | |
qualification | String | * | No, determinated by the API or the website | plane, wrongmeasurement, temporarysource, groundlevel |
qualificationVotesNumber | Integer | No, determinated by the API or the website | qualification Votes Number | |
reliability | Integer | No, but always determinated by the API and never modified | Estimated reliability that the measurement is correct. Calculated when submitted to the API as following : +1 for each filled field, + min(30,HitsNumber) if HitsNumber not null, +10 if userId not null, +20 if ManualReporting=false, +20 if MeasurementEnvironment=countryside / +10 if MeasurementEnvironment=city or ontheroad, +10 if MeasurementHeight=1. Expecting > 78 (if not qualification is set to mustbeverified and qualificationVotesNumber is set to 0) | |
atypical | Boolean | * | No, but always determinated by the API and never modified | atypical if value is not representative of an environnemental measure. No if value < 0.2 (but we should compare value to an estimated local reference ...), yes otherwise |
The OpenRadiation API attempts to return appropriate HTTP status codes for ever request
HTTP status code | Text | Description |
---|---|---|
200 | OK | API requested with success, the API will return a JSON object described as below |
201 | Created | API submitted with success and ressource created |
400 | Bad Request | The request is invalid. An error message is returned (described as below) |
401 | Unauthorized | apiKey is incorrect. An error message is returned (described as below) |
403 | Forbidden | The request is understood, but it has been refused. An error message is returned (described as below) |
404 | Not Found | The URI requested is invalid |
413 | Request Entity Too Large | The request entity is too large |
500 | Internal Server Error | Something is broken. You can send an mail to [email protected] so that we can investigate |
Error message response will look like :
{
"error": {
"code": "`An application-specific error code, expressed as a string value`",
"message": "`A short, human-readable summary of the problem`"
}
}
The endpoint for request API is https://request.openradiation.net/
By default, all requests will be limited to the 400 last measurements within the criterias (considering startTime). This max number is in the response group and defined in the properties file.
General fields in the query strings :
- response=complete : render all fields and not only stared fields (see "Available for request API" column above), available for all requests.
- withEnclosedObject=no : the enclosedObject will not be in the response group (due to length spare considerations), available for all requests.
- maxNumber=
maxNumber
: limit the number of measurements in the response to maxNumber instead of default, only available for bulk requests. This number cannot be upper than the default maxNumber limit.
To get a unique measurement having the reportUuid (reportUuid should already exist in the database):
GET /measurements/`reportUuid`?apiKey=`apiKey`
GET /measurements/`reportUuid`?apiKey=`apiKey`&response=complete
GET /measurements/`reportUuid`?apiKey=`apiKey`&response=complete&withEnclosedObject=no
Response will look like :
{
"data": {
"reportUuid": "`reportUuid`",
"latitude": `latitude`,
"longitude": `longitude`,
"value": `value`,
"startTime": "`startTime`",
"qualification": "`qualification`",
"atypical": `atypical`
}
}
To get a multiple measurements with combined complex criterias :
- with min/max bounds : value, startTime, latitude, longitude (sample : minValue/maxValue)
- with an unique criteria : userId, qualification, tag, atypical, flightId, dateOfCreation
for flightId and dateOfCreation criterias there is no default maxNumber limit
All these criterias can be combined :
GET /measurements?apiKey=`apiKey`
GET /measurements?apiKey=`apiKey`&minValue=`value`&userId=`userId`&minstartTime=`startTime`&tag=`tag`&response=complete&maxNumber=`maxNumber`&withEnclosedObject=no
GET /measurements?apiKey=`apiKey`&minStartTime=`startTime`&maxStartTime=`startTime`&qualification=`qualification`
GET /measurements?apiKey=`apiKey`&dateOfCreation=`date`
GET /measurements?apiKey=`apiKey`&dateOfCreation=`date`&withEnclosedObject=no&maxNumber=`maxNumber`
Sample : to get the last measurements all over the world
https://request.openradiation.net/measurements?apiKey=bde8ebc61cb089b8cc997dd7a0d0a434
Response will look like :
{
"maxNumber":`maxNumber`,
"data": [
{
"reportUuid": "`reportUuid`",
"latitude": `latitude`,
"longitude": `longitude`,
"value": `value`,
"startTime": "`startTime`",
"qualification": "`qualification`",
"atypical": `atypical`
},
{
"reportUuid": "`reportUuid`",
"latitude": `latitude`,
"longitude": `longitude`,
"value": `value`,
"startTime": "`startTime`",
"qualification": "`qualification`",
"atypical": `atypical`
},
...
]
}
To get all flights :
GET /flights?apiKey=`apiKey`
Response will look like :
{
"data": [
{
"flightId": "`flightId`",
"flightNumber": `flightNumber`,
"departureTime": "`departureTime`",
"arrivalTime": "`arrivalTime`",
"airportOrigin": "`airportOrigin`",
"airportDestination": "`airportDestination`",
"aircraftType": "`aircraftType`",
"firstLatitude": `firstLatitude`,
"firstLongitude": `firstLongitude`,
"midLatitude": `midLatitude`,
"midLongitude": `midLongitude`,
"lastLatitude": `lastLatitude`,
"lastLongitude": `lastLongitude`
},
{
"flightId": "`flightId`",
"flightNumber": `flightNumber`,
"departureTime": "`departureTime`",
"arrivalTime": "`arrivalTime`",
"airportOrigin": "`airportOrigin`",
"airportDestination": "`airportDestination`",
"aircraftType": "`aircraftType`",
"firstLatitude": `firstLatitude`,
"firstLongitude": `firstLongitude`,
"midLatitude": `midLatitude`,
"midLongitude": `midLongitude`,
"lastLatitude": `lastLatitude`,
"lastLongitude": `lastLongitude`
},
...
]
}
To get statistics measurement by interval :
GET /measurmentsStatisticsTotal?apiKey=`apiKey`
GET /measurmentsStatisticsTotal?apiKey=`apiKey`&userId=`<userId>`
Response will look like :
{
450
}
To get statistics measurement by interval :
GET /measurmentsStatisticsByInterval?apiKey=`apiKey`
Curl Command :
curl --location --request GET 'https://request.openradiation.staging.ul2i.fr/measurementsHistoryValue?apiKey=<apikey>' \
--header 'Content-Type: application/json' \
--data-raw '{
"interval": [0.020,0.040,0.060,0.080,0.120,0.130,0.140,0.160,0.180,0.200]
"qualification" : ['groundlevel']
}'
Response will look like :
{
"elements": {
"val_0": "416",
"val_1": "3755",
"val_2": "14931",
"val_3": "44695",
"val_4": "189031",
"val_5": "29714",
"val_6": "18005",
"val_7": "24348",
"val_8": "18285",
"val_9": "11946",
"val_10": "18294"
},
"labels": {
"val_0": "0-0.020",
"val_1": "0.020-0.040",
"val_2": "0.040-0.060",
"val_3": "0.060-0.080",
"val_4": "0.080-0.120",
"val_5": "0.120-0.130",
"val_6": "0.130-0.140",
"val_7": "0.140-0.160",
"val_8": "0.160-0.180",
"val_9": "0.180-0.200",
"val_10": "0.200 et +"
},
"qualification": [
"groundlevel"
]
}
The endpoint for submit API is https://submit.openradiation.net/.
POST /measurements
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"apiKey": "`apiKey`",
"data": {
"reportUuid": "`reportUuid`",
"latitude": `latitude`,
"longitude": `longitude`,
"value": `value`,
"startTime": "`startTime`"
....
}
}
This restricted access is only available for openradiation.org website with a special secret key
To communicate the list of users :
PUT /users
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"apiKey": "`apiKey`",
"data": [{
"userId": "`userId`",
"userPwd": "`userPwd`"
},{
....
}
]
}
To Create/Update user :
PUT /users
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"apiKey": "`apiKey`",
"userId": "`userId`",
"userPwd": "`userPwd`"
}
To Delete user :
PUT /users
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"apiKey": "`apiKey`",
"userId": "`userId`"
}
To update the qualification criteria for a unique measurement :
POST /measurements/`reportUuid`
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"apiKey": "`apiKey`",
"data": {
"qualification": "`qualification`",
"qualificationVotesNumber": `qualificationVotesNumber`
}
}
This project can be start with docker
-
install docker https://docs.docker.com/docker-for-windows/install/
-
Create your images and containers for PostreSQL 9.4 :
docker-compose up -d postgres
- copy of your dump into the postgres container :
docker cp openradiation.dmp openradiation-api_postgres_1:/openradiation.dmp
- create database openradiation :
docker ps --all //to see <postresID>
docker exec -it <postresID> bash
psql -U postgres
create database openradiation;
- dump of database :
\q
pg_restore -Fc -i -U postgres -d openradiation -c /openradiation.dmp
- create your container nodeJS 6.9.3 :
exit
docker-compose up -d app