diff --git a/odoo/addons/dsrpt_repair_config/data/repair_fsm_zone_data_atlanta.xml b/odoo/addons/dsrpt_repair_config/data/repair_fsm_zone_data_atlanta.xml index f171d77..1af7b36 100644 --- a/odoo/addons/dsrpt_repair_config/data/repair_fsm_zone_data_atlanta.xml +++ b/odoo/addons/dsrpt_repair_config/data/repair_fsm_zone_data_atlanta.xml @@ -3,6 +3,9 @@ Atlanta Metro ATL + active True diff --git a/odoo/addons/dsrpt_repair_config/models/fsm_zone.py b/odoo/addons/dsrpt_repair_config/models/fsm_zone.py index d640b42..51bbbfe 100644 --- a/odoo/addons/dsrpt_repair_config/models/fsm_zone.py +++ b/odoo/addons/dsrpt_repair_config/models/fsm_zone.py @@ -1,4 +1,8 @@ -from odoo import fields, models +import json +from urllib.parse import quote + +from odoo import api, fields, models +from odoo.exceptions import ValidationError class RepairFsmZone(models.Model): @@ -9,6 +13,12 @@ class RepairFsmZone(models.Model): name = fields.Char(required=True, tracking=True) code = fields.Char(tracking=True) + polygon_geojson = fields.Text( + string="Polygon (GeoJSON)", + required=True, + tracking=True, + help="GeoJSON Polygon geometry. Coordinates order: [longitude, latitude].", + ) state = fields.Selection( selection=[ ("draft", "Draft"), @@ -32,3 +42,79 @@ class RepairFsmZone(models.Model): def action_reset_draft(self): self.write({"state": "draft", "active": True}) + + @staticmethod + def _point_in_polygon(longitude, latitude, points): + inside = False + j = len(points) - 1 + for i, (xi, yi) in enumerate(points): + xj, yj = points[j] + intersects = ((yi > latitude) != (yj > latitude)) and ( + longitude < (xj - xi) * (latitude - yi) / ((yj - yi) or 1e-12) + xi + ) + if intersects: + inside = not inside + j = i + return inside + + def _extract_polygon_points(self): + self.ensure_one() + try: + data = json.loads(self.polygon_geojson or "") + except json.JSONDecodeError as exc: + raise ValidationError(f"Invalid GeoJSON: {exc.msg}") from exc + + ring = [] + if isinstance(data, dict): + if data.get("type") == "Feature": + data = data.get("geometry") or {} + if data.get("type") != "Polygon": + raise ValidationError("GeoJSON must be of type Polygon.") + coords = data.get("coordinates") or [] + if not coords or not isinstance(coords[0], list): + raise ValidationError("Polygon coordinates are missing.") + ring = coords[0] + elif isinstance(data, list): + ring = data + else: + raise ValidationError("Polygon must be a GeoJSON object or an array of points.") + + points = [] + for pair in ring: + if not isinstance(pair, (list, tuple)) or len(pair) < 2: + raise ValidationError("Each polygon point must be [longitude, latitude].") + points.append((float(pair[0]), float(pair[1]))) + + if len(points) >= 2 and points[0] == points[-1]: + points = points[:-1] + if len(points) < 3: + raise ValidationError("Polygon must contain at least 3 points.") + return points + + def contains_point(self, latitude, longitude): + self.ensure_one() + if latitude is None or longitude is None: + return False + points = self._extract_polygon_points() + return self._point_in_polygon(float(longitude), float(latitude), points) + + def action_open_polygon_in_map(self): + self.ensure_one() + encoded = quote(self.polygon_geojson or '{"type":"Polygon","coordinates":[[]]}') + return { + "type": "ir.actions.act_url", + "url": f"https://geojson.io/#data=data:application/json,{encoded}", + "target": "new", + } + + def action_open_point_picker_map(self): + return { + "type": "ir.actions.act_url", + "url": "https://www.openstreetmap.org", + "target": "new", + } + + @api.constrains("polygon_geojson") + def _check_polygon_geojson(self): + for rec in self: + rec._extract_polygon_points() diff --git a/odoo/addons/dsrpt_repair_config/views/repair_fsm_zone_view_form.xml b/odoo/addons/dsrpt_repair_config/views/repair_fsm_zone_view_form.xml index 40a8842..38f8bbd 100644 --- a/odoo/addons/dsrpt_repair_config/views/repair_fsm_zone_view_form.xml +++ b/odoo/addons/dsrpt_repair_config/views/repair_fsm_zone_view_form.xml @@ -9,6 +9,8 @@