@@ -79,12 +78,13 @@
data-bs-toggle="collapse"
data-bs-target="#map-view"
>
- Просмотр карты
+ Глобальная карта
- В режиме просмотра карты можно выбирать слои для отображения: слой
+ В режиме глобальной карты отображаются все карты, загруженные в систему
+ можно выбирать слои для отображения: слой
изображения самого снимка, и слой найденных объектов. Данные слои
накладываются поверх стандартной карты OpenStreetMap.
@@ -127,8 +127,7 @@
В режиме просмотра объекта можно посмотреть все параметры найденного
объекта, отметить его на карте с помощью клавиши "Показать на
- карте", а так же открыть отчет изображения, на котором был найден
- объект.
+ карте".
diff --git a/client/src/components/routes/map/MapComponent.vue b/client/src/components/routes/map/MapComponent.vue
index 9372422..054eac0 100644
--- a/client/src/components/routes/map/MapComponent.vue
+++ b/client/src/components/routes/map/MapComponent.vue
@@ -1,29 +1,26 @@
diff --git a/client/src/components/routes/map/api.ts b/client/src/components/routes/map/api.ts
deleted file mode 100644
index 53457f5..0000000
--- a/client/src/components/routes/map/api.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import axios from "axios";
-
-import { baseURL } from "@/api";
-import { ObjectData } from "@/types/objects";
-
-export async function getMapData(id: string): Promise
{
- return (await axios.get(baseURL + "/objects/" + id)).data;
-}
\ No newline at end of file
diff --git a/client/src/components/routes/maps/MapsListComponent.vue b/client/src/components/routes/maps/MapsListComponent.vue
index 7b472e2..b2a021b 100644
--- a/client/src/components/routes/maps/MapsListComponent.vue
+++ b/client/src/components/routes/maps/MapsListComponent.vue
@@ -59,18 +59,16 @@ const columnDefs: ColDef[] = [
{ headerName: "Имя", field: "name", flex: 3, minWidth: 180 },
{
headerName: "Дата загрузки",
- field: "date",
+ field: "updateDatetime",
flex: 5,
minWidth: 180,
valueFormatter: dateFormatter,
},
{
- headerName: "Размер",
- field: "size",
+ headerName: "Id загрузившего пользователя",
+ field: "updateUserId",
flex: 5,
- minWidth: 180,
- valueFormatter: ({ value }: { value: number }) =>
- `${Math.round(value / 1048576)} Мб`,
+ minWidth: 100,
},
{
headerName: "Обработано",
@@ -94,14 +92,9 @@ const columnDefs: ColDef[] = [
button: "btn-secondary",
hide: (data) => !(data.ready && data.sliced),
onClicked: (action, data) => {
- router.push({
- name: routeNames.Map,
- params: {
- y: data.location.coordinates[0][0],
- x: data.location.coordinates[0][1],
- },
- });
- },
+ router.push({ name: routeNames.Map, params: { y: data.center[0],
+ x: data.center[1] } })
+ }
},
{
tooltip: "Удалить карту",
diff --git a/client/src/components/routes/maps/api.ts b/client/src/components/routes/maps/api.ts
index 507c164..b5d70d8 100644
--- a/client/src/components/routes/maps/api.ts
+++ b/client/src/components/routes/maps/api.ts
@@ -7,5 +7,5 @@ export async function getMapsInfo(): Promise {
}
export function deleteMap(id: string) {
- return axios.delete(baseURL + "/images/delete_image/" + id);
+ return axios.delete(baseURL + "/images/image/" + id);
}
diff --git a/client/src/components/routes/object/ObjectComponent.vue b/client/src/components/routes/object/ObjectComponent.vue
index 2f969f9..bd46286 100644
--- a/client/src/components/routes/object/ObjectComponent.vue
+++ b/client/src/components/routes/object/ObjectComponent.vue
@@ -13,39 +13,38 @@
diff --git a/client/src/components/routes/object/api.ts b/client/src/components/routes/object/api.ts
index 357de5a..9397d0a 100644
--- a/client/src/components/routes/object/api.ts
+++ b/client/src/components/routes/object/api.ts
@@ -1,15 +1,9 @@
-import { ObjectData } from "@/types/objects";
+import { ObjectInfo } from "@/types/objects";
import axios from "axios";
import { baseURL } from "@/api";
-export async function getObjectData(
- id: string,
- name: string,
- objectIndex: string
-): Promise {
+export async function getObjectsInfo(id: string): Promise {
return (
- await axios.get(
- baseURL + "/objects/" + id + "/" + name + "/" + objectIndex
- )
+ await axios.get(baseURL + "/objects/object/" + id)
).data;
}
diff --git a/client/src/components/routes/objects/ObjectsListComponent.vue b/client/src/components/routes/objects/ObjectsListComponent.vue
index 4322bd7..55cad10 100644
--- a/client/src/components/routes/objects/ObjectsListComponent.vue
+++ b/client/src/components/routes/objects/ObjectsListComponent.vue
@@ -29,22 +29,20 @@ const router = useRouter();
const columnDefs: ColDef[] = [
{ headerName: "Id", field: "id", flex: 2, minWidth: 120 },
- { headerName: "Название", field: "name", flex: 4, minWidth: 180 },
- { headerName: "Индекс", field: "objectIndex", flex: 4, minWidth: 180 },
- { headerName: "Площадь", field: "area", flex: 4, minWidth: 120 },
+ { headerName: "Тип", field: "type", flex: 4, minWidth: 80 },
+ { headerName: "Название", field: "name", flex: 4, minWidth: 80 },
{
headerName: "Дата загрузки",
- field: "uploadDate",
+ field: "updateDatetime",
flex: 5,
- valueFormatter: dateFormatter,
minWidth: 200,
+ valueFormatter: dateFormatter,
},
{
- headerName: "Дата обнаружения",
- field: "detectDate",
+ headerName: "Id загрузившего пользователя",
+ field: "updateUserId",
flex: 5,
- valueFormatter: dateFormatter,
- minWidth: 200,
+ minWidth: 100,
},
{
...getActionsColDef([
@@ -55,11 +53,7 @@ const columnDefs: ColDef[] = [
onClicked: (action, data) =>
router.push({
name: routeNames.Object,
- params: {
- id: data.id,
- name: data.name,
- objectIndex: data.objectIndex,
- },
+ params: { id: data.id },
}),
},
]),
diff --git a/client/src/components/routes/objects/api.ts b/client/src/components/routes/objects/api.ts
index 757644a..81e196a 100644
--- a/client/src/components/routes/objects/api.ts
+++ b/client/src/components/routes/objects/api.ts
@@ -1,7 +1,7 @@
-import { ObjectData } from "@/types/objects";
+import { ObjectInfo } from "@/types/objects";
import axios from "axios";
import { baseURL } from "@/api";
-export async function getObjectsInfo(): Promise {
- return (await axios.get(baseURL + "/objects/")).data;
+export async function getObjectsInfo(): Promise {
+ return (await axios.get(baseURL + "/objects/")).data;
}
diff --git a/client/src/router/index.ts b/client/src/router/index.ts
index b27ee84..b72a066 100644
--- a/client/src/router/index.ts
+++ b/client/src/router/index.ts
@@ -18,7 +18,7 @@ export const routePaths = {
[routeNames.ObjectsList]: "/objects",
[routeNames.Upload]: "/upload",
[routeNames.Map]: "/map/:y?/:x?",
- [routeNames.Object]: "/object/:id/:name/:objectIndex",
+ [routeNames.Object]: "/object/:id",
[routeNames.Home]: "/home",
[routeNames.Auth]: "/auth",
[routeNames.Auth]: "/Users",
@@ -59,9 +59,7 @@ export const routes: RouteRecordRaw[] = [
path: routePaths[routeNames.Object],
component: () => import("@/views/ObjectView.vue"),
props: (route) => ({
- id: route.params.id,
- name: route.params.name,
- objectIndex: route.params.objectIndex,
+ id: route.params.id
}),
},
{
diff --git a/client/src/types/maps.ts b/client/src/types/maps.ts
index c404c47..adb934d 100644
--- a/client/src/types/maps.ts
+++ b/client/src/types/maps.ts
@@ -1,9 +1,10 @@
export interface MapInfo {
id: string;
name: string;
- date: string;
- size: number;
- location: {type: string, coordinates: number[][]};
+ updateUserId: string;
+ updateDatetime: string;
+ center: [number, number];
+ coordinates: number[][];
ready: boolean;
sliced: boolean;
}
diff --git a/client/src/types/objects.ts b/client/src/types/objects.ts
index 4e64f17..6b416b3 100644
--- a/client/src/types/objects.ts
+++ b/client/src/types/objects.ts
@@ -1,24 +1,10 @@
export interface ObjectInfo {
id: string;
- name: string;
- objectIndex: string;
- area: number;
- uploadDate: string;
- detectDate: string;
-}
-
-export interface ObjectData {
- id: string;
- name: string;
- objectIndex: string;
- area: number;
- coordinates: [number, number];
- uploadDate: string;
- detectDate: string;
-}
-
-export interface ObjectsMapData {
+ type: string;
name: string;
color: string;
- polygons: number[][];
-}
+ updateUserId: string;
+ updateDatetime: string;
+ center: [number, number];
+ coordinates: number[][];
+}
\ No newline at end of file
diff --git a/client/src/views/ObjectView.vue b/client/src/views/ObjectView.vue
index 997f7ca..dab2ee1 100644
--- a/client/src/views/ObjectView.vue
+++ b/client/src/views/ObjectView.vue
@@ -1,6 +1,6 @@
-
+
@@ -10,7 +10,7 @@
diff --git a/docker-compose.yml b/docker-compose.yml
index 4bba580..b469058 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -50,7 +50,6 @@ services:
FLASK_PORT: 80
MONGO_URI: "mongodb://mongo:27017/db"
REDIS_URI: "redis://redis:6379/0"
- DEBUG: False
depends_on:
- celery-image
- celery-slice
diff --git a/server/app/db.py b/server/app/db.py
index 3ad1d14..4f5b29e 100644
--- a/server/app/db.py
+++ b/server/app/db.py
@@ -11,6 +11,9 @@ def get_db():
client = pymongo.MongoClient(app.config.get('MONGO_URI')) # config['PROD']['DB_URI']
# Если есть база данных ecologyDB
db = g.database = client.get_database('ecologyDB')
+ # Создание Геоиндексов.
+ db.maps.files.create_index([("center", pymongo.GEOSPHERE)])
+ db.objects.create_index([("center", pymongo.GEOSPHERE)])
return db
diff --git a/server/app/routes/__init__.py b/server/app/routes/__init__.py
index 9fa650d..5aeabff 100644
--- a/server/app/routes/__init__.py
+++ b/server/app/routes/__init__.py
@@ -4,6 +4,7 @@
from .user import api as user_api
from .auth import api as auth_api
from .objects import api as objects_api
+from .tiles import api as tiles_api
api = Api(
title="Ecology API",
@@ -15,4 +16,5 @@
api.add_namespace(user_api)
api.add_namespace(auth_api)
api.add_namespace(images_api)
+api.add_namespace(tiles_api)
api.add_namespace(objects_api)
diff --git a/server/app/routes/images.py b/server/app/routes/images.py
index 52468b6..c010b1a 100644
--- a/server/app/routes/images.py
+++ b/server/app/routes/images.py
@@ -20,20 +20,20 @@
def get_images_list():
- images = []
- for img in db.images.find({}):
- images.append({
- "id": str(img["_id"]),
- "name": img["name"],
- 'uploadDate': img["upload_date"],
- 'size': maps_fs.find_one({'_id': img["fs_id"]}).length,
- "location": img["location"],
- "ready": img["ready"],
- "sliced": img["sliced"]
+ maps = []
+ for map in db.maps.files.find({}):
+ maps.append({
+ "id": str(map["_id"]),
+ "name": map["name"],
+ "updateUserId": map["update"]["user_id"],
+ "updateDatetime": map["update"]["datetime"],
+ "coordinates": map["coordinates"],
+ # Переворачиваем, чтобы получить [lat, lng] для leaflet
+ "center": [map["center"][1], map["center"][0]],
+ "ready": map["ready"],
+ "sliced": map["sliced"]
})
-
- return images
-
+ return maps
@api.route('/')
class ImagesList(Resource):
@@ -41,25 +41,21 @@ def get(self):
return get_images_list()
def post(self):
- image = request.files['image']
- file_id = maps_fs.put(image, filename=image.filename, chunk_size=256 * 1024)
+ new_map = request.files['image']
img_name = request.form.get('name')
- item = {
- "location": {"type": "Polygon", "coordinates": []},
- "fs_id": file_id,
- "objects": [],
- "upload_date": str(arrow.now().to('UTC')),
- "detect_date": "",
- "name": img_name,
- 'ready': False,
- 'sliced': False
- }
-
- result = db.images.insert_one(item)
- img_id = result.inserted_id
-
- slice.delay(str(img_id))
- process_image.delay(str(img_id))
+ file_id = maps_fs.put(
+ new_map, filename=request.form.get('name'), chunk_size= 256 * 1024,
+ name = img_name,
+ center = [0.5, 0.5],
+ coordinates = [[0, 1], [1, 1], [1, 0], [0, 0]],
+ # TO DO: normal user_id.
+ update = {"user_id": 0, "datetime": str(arrow.now().to('UTC'))},
+ ready = False,
+ sliced = False
+ )
+
+ slice.delay(str(file_id))
+ process_image.delay(str(file_id))
return jsonify({'message': 'Image added successfully'})
@@ -68,65 +64,48 @@ def post(self):
class ImageFinder(Resource):
def get(self, y, x, r):
x, y, r = float(x), float(y), float(r)
- # TO DO: после рефакторинга бд должно находится НОРМАЛЬНО, а не в цикле :(
- images = []
- for img in db.images.find({}):
- # Координаты в [y, x], так как leaflet работает в [lat, long].
- if (img["sliced"]):
- y_point_img, x_point_img = img["location"]["coordinates"][0]
- if (x_point_img - x) ** 2 + (y_point_img - y) ** 2 <= r ** 2:
- images.append({
- "id": str(img["_id"]),
- "name": img["name"],
- 'uploadDate': img["upload_date"],
- 'size': maps_fs.find_one({'_id': img["fs_id"]}).length,
- "location": img["location"],
- "ready": img["ready"],
- "sliced": img["sliced"]
- })
- return images
-
-
-@api.route('/tile_map_resource/')
-class TileResources(Resource):
- def get(self, img_id):
- tile_map_resource = db.images.find_one(ObjectId(img_id))["tile_map_resource"]
- if tile_map_resource is None:
- abort(404)
- else:
- return db.images.find_one(ObjectId(img_id))["tile_map_resource"]
+
+ maps_info = db.maps.files.find({
+ "center": {
+ "$nearSphere": {
+ "$geometry": {
+ "type": "Point",
+ "coordinates": [x, y]
+ },
+ "$maxDistance": r,
+ }
+ },
+ "sliced": True
+ })
+
+ maps = []
+ for map in maps_info:
+ maps.append({
+ "id": str(map["_id"]),
+ "name": map["name"],
+ "updateUserId": map["update"]["user_id"],
+ "updateDatetime": map["update"]["datetime"],
+ "coordinates": map["coordinates"],
+ # Переворачиваем, чтобы получить [lat, lng] для leaflet
+ "center": [map["center"][1], map["center"][0]],
+ "ready": map["ready"],
+ "sliced": map["sliced"]
+ })
+ return maps
@api.route('/image/')
class Image(Resource):
def delete(self, img_id):
- image_info = db.images.find_one(ObjectId(img_id))
- if (image_info):
- fs_id = image_info["fs_id"]
+ map_info = db.maps.files.find_one(ObjectId(img_id))
- db.images.delete_one({"_id": ObjectId(img_id)})
+ if map_info:
+ maps_fs.delete(ObjectId(img_id))
+ for tile in db.tiles.files.find({"image_id": ObjectId(img_id)}):
+ tiles_fs.delete(tile["_id"])
- maps_fs.delete(fs_id)
- for tile in tiles_fs.find({"image_id": ObjectId(img_id)}):
- tiles_fs.delete(tile._id)
-
- socketio.emit("images", get_images_list())
+ socketio.emit("images", get_images_list()) #update sockets
return jsonify({'message': 'Image deleted successfully'})
+
return 'OK'
-
-# Маршрут для leaflet-а, возвращает кусочки для отображения.
-@api.route("/tile////")
-class Tile(Resource):
- def get(self, img_id, z, x, y):
- tile = tiles_fs.find_one({'image_id': ObjectId(img_id), 'z': z, 'x': x, 'y': y})
- if tile:
- return send_file(io.BytesIO(tile.read()), mimetype='image/png')
- else:
- return 'OK'
-
-
-@api.route('/objects/')
-class ObjectsFinder(Resource):
- def get(self, img_id):
- return db.images.find_one(ObjectId(img_id))["objects"]
diff --git a/server/app/routes/objects.py b/server/app/routes/objects.py
index 898665b..cae4922 100644
--- a/server/app/routes/objects.py
+++ b/server/app/routes/objects.py
@@ -17,96 +17,66 @@
class ObjectsList(Resource):
def get(self):
objects = []
- for img in db.images.find({}):
- img_objects_types = img["objects"]
- for img_objects in img_objects_types:
- for i in range(len(img_objects['area'])):
- objects.append({
- "id": str(img["_id"]),
- "name": img_objects["name"],
- "objectIndex": i,
- "area": img_objects['area'][i],
- "uploadDate": img["upload_date"],
- "detectDate": img["detect_date"]
- })
+ for map_object in db.objects.find({}):
+ objects.append({
+ "id": str(map_object["_id"]),
+ "type": map_object["type"],
+ "name": map_object["name"],
+ "color": map_object["color"],
+ "updateUserId": map_object["update"]["user_id"],
+ "updateDatetime": map_object["update"]["datetime"],
+ "coordinates": map_object["coordinates"],
+ # Переворачиваем, чтобы получить [lat, lng] для leaflet
+ "center": [map_object["center"][1], map_object["center"][0]],
+ })
return objects
-@api.route('/')
+@api.route('/object/')
class ImageObjects(Resource):
- def get(self, img_id):
- img = db.images.find_one(ObjectId(img_id))
- img_objects_types = img["objects"]
-
- objects = []
- for img_objects in img_objects_types:
- for i in range(len(img_objects['area'])):
- coordinates = [0, 0]
- for polygon_point in img_objects['polygons'][int(i)]:
- coordinates[0] += polygon_point[0]
- coordinates[1] += polygon_point[1]
- coordinates[0] /= len(img_objects['polygons'][int(i)])
- coordinates[1] /= len(img_objects['polygons'][int(i)])
-
- objects.append({
- "id": str(img_id),
- "name": img_objects["name"],
- "objectIndex": i,
- "area": img_objects["area"][i],
- "coordinates": coordinates,
- "uploadDate": img["upload_date"],
- "detectDate": img["detect_date"]
- })
- return objects
-
-
-@api.route('///')
-class Object(Resource):
- def get(self, img_id, object_name, object_index):
- img = db.images.find_one(ObjectId(img_id))
- img_objects_types = img["objects"]
-
- area = 0
- for img_objects in img_objects_types:
- if (img_objects['name'] == object_name):
- area = img_objects['area'][int(object_index)]
- coordinates = [0, 0]
- for polygon_point in img_objects['polygons'][int(object_index)]:
- coordinates[0] += polygon_point[0]
- coordinates[1] += polygon_point[1]
- coordinates[0] /= len(img_objects['polygons'][int(object_index)])
- coordinates[1] /= len(img_objects['polygons'][int(object_index)])
- break
-
- result = {
- "id": str(img_id),
- "name": object_name,
- "objectIndex": object_index,
- "area": area,
- "coordinates": coordinates,
- "uploadDate": img["upload_date"],
- "detectDate": img["detect_date"]
+ def get(self, obj_id):
+ map_object = db.objects.find_one(ObjectId(obj_id))
+ return {
+ "id": str(map_object["_id"]),
+ "type": map_object["type"],
+ "name": map_object["name"],
+ "color": map_object["color"],
+ "updateUserId": map_object["update"]["user_id"],
+ "updateDatetime": map_object["update"]["datetime"],
+ "coordinates": map_object["coordinates"],
+ # Переворачиваем, чтобы получить [lat, lng] для leaflet
+ "center": [map_object["center"][1], map_object["center"][0]],
}
- return result
@api.route('/near///')
class ObjectsNear(Resource):
def get(self, y, x, r):
x, y, r = float(x), float(y), float(r)
- # TO DO: после рефакторинга бд должно находится НОРМАЛЬНО, а не в цикле :(
+
+ objects_info = db.objects.find({
+ "center": {
+ "$nearSphere": {
+ "$geometry": {
+ "type": "Point",
+ "coordinates": [x, y]
+ },
+ "$maxDistance": r,
+ }
+ }
+ })
+
objects = []
- for img in db.images.find({}):
- img_objects_types = img["objects"]
- for img_objects in img_objects_types:
- for i in range(len(img_objects['area'])):
- # Проверяем только одну точку у объектов на наличие в окружности, потому что проверять все
- # точки слишом затратно.
- y_point_obj, x_point_obj = img_objects["polygons"][i][0]
- if (x_point_obj - x) ** 2 + (y_point_obj - y) ** 2 <= r ** 2:
- objects.append({
- "name": img_objects["name"],
- "color": img_objects["color"],
- "polygons": img_objects["polygons"][i]
- })
+ for map_object in objects_info:
+ objects.append({
+ "id": str(map_object["_id"]),
+ "type": map_object["type"],
+ "name": map_object["name"],
+ "color": map_object["color"],
+ "updateUserId": map_object["update"]["user_id"],
+ "updateDatetime": map_object["update"]["datetime"],
+ "coordinates": map_object["coordinates"],
+ # Переворачиваем, чтобы получить [lat, lng] для leaflet
+ "center": [map_object["center"][1], map_object["center"][0]],
+ })
return objects
diff --git a/server/app/routes/tiles.py b/server/app/routes/tiles.py
new file mode 100644
index 0000000..e94e3a1
--- /dev/null
+++ b/server/app/routes/tiles.py
@@ -0,0 +1,30 @@
+import io
+import arrow
+from flask import jsonify, request, send_file, abort
+from flask_restx import Namespace, Resource
+from redis.client import StrictRedis
+from werkzeug.local import LocalProxy
+from bson.objectid import ObjectId
+
+from app import socketio
+from app.db import get_db, get_tiles, get_maps, get_redis
+from app.tasks import process_image
+from app.tasks import slice
+
+db = LocalProxy(get_db)
+tiles_fs = LocalProxy(get_tiles)
+maps_fs = LocalProxy(get_maps)
+redis: StrictRedis = LocalProxy(get_redis)
+
+api = Namespace("tiles", description="Операции с тайлами изображений")
+
+
+# Маршрут для leaflet-а, возвращает кусочки для отображения.
+@api.route("/tile////")
+class Tile(Resource):
+ def get(self, img_id, z, x, y):
+ tile = tiles_fs.find_one({'image_id': ObjectId(img_id), 'z': z, 'x': x, 'y': y})
+ if tile:
+ return send_file(io.BytesIO(tile.read()), mimetype='image/png')
+ else:
+ return 'OK'
diff --git a/server/app/tasks.py b/server/app/tasks.py
index 35a047b..ada1e35 100644
--- a/server/app/tasks.py
+++ b/server/app/tasks.py
@@ -12,14 +12,14 @@ def delete_all_data_in_db_and_fs():
@app.task(name='image_process', queue="image_process")
-def process_image(img_id):
+def process_image(image_id):
pass
@app.task(name='slice', queue="slice")
-def slice(img_id):
+def slice(image_id):
pass
@app.task(name='deforestation', queue="image_process")
-def deforestation(img_id):
+def deforestation(map_id):
pass
diff --git a/server/requirements.txt b/server/requirements.txt
index 85ccc8e..f274cb8 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -1,3 +1,4 @@
+Werkzeug==2.3.3
Flask==2.2.3
Flask-Cors==3.0.10
pymongo==4.3.3
diff --git a/worker/app/image_processing/objects/object_base.py b/worker/app/image_processing/objects/object_base.py
index 7559647..396a195 100644
--- a/worker/app/image_processing/objects/object_base.py
+++ b/worker/app/image_processing/objects/object_base.py
@@ -15,6 +15,7 @@ def __init__(self, img_id, image_bytes):
self.img_id = img_id
self.image_bytes = image_bytes
self.polygons = []
+ self.centers = []
self.area = []
self.max_area = 0
@@ -47,10 +48,20 @@ def find_geo_polygons(self, contours_of_object):
for line in contours_of_object:
# Преобразовываем координаты каждой точки из пикселей в широту и долготу.
line_arr = []
+ curr_center = [0, 0]
for point in line:
x_pix, y_pix = point[0]
- line_arr.append(coord_transformer.pixel_xy_to_lat_long(x_pix, y_pix))
+ point_transf = coord_transformer.pixel_xy_to_lat_long(x_pix, y_pix)
+ line_arr.append(point_transf)
+
+ curr_center[0] += point_transf[0]
+ curr_center[1] += point_transf[1]
+
+ # Переворачиваем координаты из [lat, lng] получаем [lng, lat]
+ curr_center[1], curr_center[0] = curr_center[0] / len(line_arr), curr_center[1] / len(line_arr)
+ self.centers.append(curr_center)
+
self.polygons.append(line_arr)
self.update(step_progress)
@@ -100,6 +111,7 @@ def filter_polygons_by_area(self, min_area):
if (self.area[i] < min_area):
self.area.pop(i)
self.polygons.pop(i)
+ self.centers.pop(i)
else:
i += 1
@@ -128,36 +140,27 @@ def after_end_of_process(self):
'''
db = local.db
redis = local.redis
- image_info = db.images.find_one(ObjectId(self.img_id))
+
queue_item = f'queue:{self.img_id}'
if (len(self.polygons) > 0):
- # Формируем словарь найденных объектов.
- object_dict = {
- 'name': self.name,
- 'color': self.color,
- 'polygons': self.polygons,
- 'area': self.area
- }
-
- # Добавляем словарь найденных объектов в базу данных.
- objects_list = image_info['objects']
- objects_list.append(object_dict)
- db.images.update_one({"_id": image_info['_id']}, {"$set": {"objects": objects_list}})
- db.images.update_one({"_id": image_info['_id']}, {"$set": {"detect_date": str(arrow.now().to('UTC'))}})
+ # Добавляем найденные объекты в базу данных.
+ objects = []
+ for i in range(len(self.polygons)):
+ objects.append({
+ # TO DO: name == type?
+ "type": self.name,
+ "name": self.name,
+ "color": self.color,
+ # TO DO: -1 индекс - это автоматическая обработка?
+ "update": {"user_id": -1, "datetime": str(arrow.now().to('UTC'))},
+ "coordinates": self.polygons[i],
+ "center": self.centers[i],
+ })
+ db.objects.insert_many(objects)
# Удаляем запись в redis-е, если обработки всех объектов завершились.
redis.hset(queue_item, 'processing_functions', int(redis.hget(queue_item, 'processing_functions')) - 1)
if int(redis.hget(queue_item, 'processing_functions')) == 0:
redis.delete(queue_item)
- db.images.update_one({"_id": image_info['_id']}, {"$set": {"ready": True}})
-
- def get_object_by_index(self, index):
- '''
- Метод, который возвращает одну объект из найденных на изображении.
- '''
- return {
- 'index': index,
- 'polygon': self.polygons[index],
- 'area': self.area[index]
- }
+ db.maps.files.update_one({"_id": ObjectId(self.img_id)}, {"$set": {"ready": True}})
diff --git a/worker/app/image_processing/objects/object_deforestation.py b/worker/app/image_processing/objects/object_deforestation.py
index e013c20..64348b3 100644
--- a/worker/app/image_processing/objects/object_deforestation.py
+++ b/worker/app/image_processing/objects/object_deforestation.py
@@ -52,12 +52,10 @@ def find_contours_of_object(self):
@staticmethod
@app.task(name='deforestation_find', queue="image_process")
def create_and_process(img_id):
- db = local.db
maps_fs = local.maps_fs
- image_info = db.images.find_one(ObjectId(img_id))
# Получаем саму картинку из GridFS.
- image_bytes = maps_fs.get(ObjectId(image_info['fs_id'])).read()
+ image_bytes = maps_fs.find_one(ObjectId(img_id)).read()
deforestation_object = ObjectDeforestation(img_id, image_bytes)
ObjectBase.process_object(deforestation_object)
diff --git a/worker/app/image_processing/objects/object_forest.py b/worker/app/image_processing/objects/object_forest.py
index 606afba..128f392 100644
--- a/worker/app/image_processing/objects/object_forest.py
+++ b/worker/app/image_processing/objects/object_forest.py
@@ -47,12 +47,10 @@ def find_contours_of_object(self):
@staticmethod
@app.task(name='forest_find', queue="image_process")
def create_and_process(img_id):
- db = local.db
maps_fs = local.maps_fs
- image_info = db.images.find_one(ObjectId(img_id))
# Получаем саму картинку из GridFS.
- image_bytes = maps_fs.get(ObjectId(image_info['fs_id'])).read()
+ image_bytes = maps_fs.get(ObjectId(img_id)).read()
forest_object = ObjectForest(img_id, image_bytes)
ObjectBase.process_object(forest_object)
diff --git a/worker/app/image_processing/utility.py b/worker/app/image_processing/utility.py
index 5d56ed7..6e539cc 100644
--- a/worker/app/image_processing/utility.py
+++ b/worker/app/image_processing/utility.py
@@ -92,13 +92,10 @@ def parse_xml_slice(path_to_xml):
coordinates = root.findall("BoundingBox")[0].attrib
- return {
- "type": "Polygon",
- "coordinates": [
+ return [
# Координаты в [y, x], так как leaflet работает в [lat, long].
[float(coordinates["miny"]), float(coordinates["minx"])],
[float(coordinates["miny"]), float(coordinates["maxx"])],
[float(coordinates["maxy"]), float(coordinates["maxx"])],
[float(coordinates["maxy"]), float(coordinates["minx"])],
]
- }
diff --git a/worker/app/tasks/image_process.py b/worker/app/tasks/image_process.py
index 5946f92..d69ba83 100644
--- a/worker/app/tasks/image_process.py
+++ b/worker/app/tasks/image_process.py
@@ -7,10 +7,11 @@
@app.task(name='image_process', queue="image_process")
-def process_image(img_id: str):
+def process_image(map_id: str):
db = local.db
redis = local.redis
- image_info = db.images.find_one(ObjectId(img_id))
+
+ map_info = db.maps.files.find_one(ObjectId(map_id))
#
## Список объектов для поиска
@@ -19,12 +20,12 @@ def process_image(img_id: str):
#
# Создаем запись в redis-е для отображения очереди на клиенте.
- queue_item = f'queue:{img_id}'
+ queue_item = f'queue:{map_id}'
redis.hset(queue_item, mapping={
- 'id': str(img_id),
+ 'id': map_id,
'progress': 0,
- 'name': image_info['name'],
- 'uploadDate': image_info['upload_date'],
+ 'name': map_info['name'],
+ 'uploadDate': map_info['update']["datetime"],
'status': 'enqueued',
'processing_functions_immut': len(objects),
'processing_functions': len(objects)
@@ -33,6 +34,6 @@ def process_image(img_id: str):
# Запуск обработчиков объектов.
for object_class in objects:
- object_class.create_and_process.delay(img_id)
+ object_class.create_and_process.delay(map_id)
return "Done"
\ No newline at end of file
diff --git a/worker/app/tasks/slice.py b/worker/app/tasks/slice.py
index 7f58941..b2ddea7 100644
--- a/worker/app/tasks/slice.py
+++ b/worker/app/tasks/slice.py
@@ -13,7 +13,7 @@
logger = get_task_logger(__name__)
@app.task(name='slice', queue="slice")
-def slice(img_id: str):
+def slice(image_id: str):
"""
Нарезать geotiff в базе данных с индексом id на кусочки и положить их в gridfs с именем
___.png
@@ -23,17 +23,14 @@ def slice(img_id: str):
tiles_fs = local.tiles_fs
redis = local.redis
- # Получаем запись из бд с информацией по изображению.
- image_info = db.images.find_one(ObjectId(img_id))
-
# Получаем саму картинку из GridFS.
- image_bytes = maps_fs.get(image_info['fs_id']).read()
+ image_bytes = maps_fs.get(ObjectId(image_id)).read()
slicers = len(redis.keys('slice_queue:*'))
# Нарезаем на фрагменты.
sliceToTiles(
- img_id, image_bytes, f'./{img_id}',
+ image_id, image_bytes, f'./{image_id}',
optionsSliceToTiles= {
"nb_processes": max(1, multiprocessing.cpu_count() // (1 + slicers)),
"zoom": [config.MIN_ZOOM, config.MAX_ZOOM]
@@ -41,12 +38,11 @@ def slice(img_id: str):
)
# Удаляем фрагменты, если они уже были в GridFS.
- cursor = tiles_fs.find({"image_id": img_id})
- for document in cursor:
+ for document in tiles_fs.find({"image_id": image_id}):
tiles_fs.delete(document["_id"])
# Добавляем все фрагменты в GridFS.
- for root, _, files in os.walk(img_id):
+ for root, _, files in os.walk(image_id):
path = root.split(os.sep)
for file in files:
# Сами фрагменты лежат по пути /{z}/{x}/{y}.png, но нужно отсечь доп. файлы
@@ -56,25 +52,32 @@ def slice(img_id: str):
file_content = f.read()
tiles_fs.put(
file_content,
- image_id=ObjectId(img_id),
- z=int(path[1]),
- x=int(path[2]),
- y=int(file.split('.')[0])
+ image_id = ObjectId(image_id),
+ z = int(path[1]),
+ x = int(path[2]),
+ y = int(file.split('.')[0])
)
# Добавляем данные для отображения изображения.
- location = parse_xml_slice(f'{img_id}/tilemapresource.xml')
- db.images.update_one({"_id": image_info["_id"]}, {"$set": {"location": location}})
+ coordinates = parse_xml_slice(f'{image_id}/tilemapresource.xml')
+ center = [0, 0]
+ for i in range(len(coordinates)):
+ center[0] += coordinates[i][0]
+ center[1] += coordinates[i][1]
+ # Переворачиваем координаты из [lat, lng] получаем [lng, lat]
+ center[1], center[0] = center[0] / 4, center[1] / 4
+ # coordinates в порядке [lat, lng]
+ db.maps.files.update_one({"_id": ObjectId(image_id)}, {"$set": {"coordinates": coordinates, "center": center}})
# Удаляем временную папку со слайсами.
- for root, dirs, files in os.walk(img_id, topdown=False):
+ for root, dirs, files in os.walk(image_id, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
- os.rmdir(img_id)
+ os.rmdir(image_id)
- redis.delete(f'slice_queue:{img_id}')
- db.images.update_one({"_id": image_info["_id"]}, {"$set": {"sliced": True}})
+ redis.delete(f'slice_queue:{image_id}')
+ db.maps.files.update_one({"_id": ObjectId(image_id)}, {"$set": {"sliced": True}})
return "Done"