Moved widget to ZMI module
authorThierry Florac <thierry.florac@onf.fr>
Wed, 11 Jul 2018 11:40:09 +0200
changeset 55 829abfdd6d27
parent 54 4ce98983666a
child 56 37970a213463
Moved widget to ZMI module
src/pyams_gis/widget/__init__.py
src/pyams_gis/widget/area.py
src/pyams_gis/widget/point.py
src/pyams_gis/widget/templates/geoarea-input.pt
src/pyams_gis/widget/templates/geopoint-input.pt
src/pyams_gis/zmi/widget/__init__.py
src/pyams_gis/zmi/widget/area.py
src/pyams_gis/zmi/widget/point.py
src/pyams_gis/zmi/widget/templates/geoarea-input.pt
src/pyams_gis/zmi/widget/templates/geopoint-input.pt
--- a/src/pyams_gis/widget/__init__.py	Wed Jul 11 11:39:49 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#
-# Copyright (c) 2008-2017 Thierry Florac <tflorac AT ulthar.net>
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-
-__docformat__ = 'restructuredtext'
-
-
-# import standard library
-
-# import interfaces
-
-# import packages
--- a/src/pyams_gis/widget/area.py	Wed Jul 11 11:39:49 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-#
-# Copyright (c) 2008-2017 Thierry Florac <tflorac AT ulthar.net>
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-
-__docformat__ = 'restructuredtext'
-
-
-# import standard library
-import json
-
-# import interfaces
-from pyams_form.interfaces.form import IFormLayer, IForm
-from pyams_gis.interfaces import IGeoArea
-from pyams_gis.interfaces.widget import IGeoAreaWidget
-from pyams_gis.schema import IGeoAreaField
-from pyams_utils.interfaces.data import IObjectData
-from z3c.form.interfaces import IFieldWidget, IObjectFactory
-
-# import packages
-from pyams_form.widget import widgettemplate_config
-from pyams_gis.area import GeoArea
-from pyams_utils.adapter import adapter_config
-from z3c.form.browser.object import ObjectWidget
-from z3c.form.object import getIfName
-from z3c.form.widget import FieldWidget
-from zope.interface import implementer_only, alsoProvides, Interface
-
-
-@adapter_config(name=getIfName(IGeoArea),
-                context=(Interface, IFormLayer, IForm, IGeoAreaWidget), provides=IObjectFactory)
-class GeoAreaObjectFactory(object):
-    """GeoArea object factory"""
-
-    def __init__(self, context, request, form, widget):
-        self.context = context
-        self.request = request
-        self.form = form
-        self.widget = widget
-
-    def __call__(self, data):
-        return GeoArea()
-
-
-@widgettemplate_config(mode='input', template='templates/geoarea-input.pt', layer=IFormLayer)
-@implementer_only(IGeoAreaWidget)
-class GeoAreaWidget(ObjectWidget):
-    """GeoArea widget"""
-
-    def updateWidgets(self, setErrors=True):
-        super(GeoAreaWidget, self).updateWidgets()
-        widgets = self.subform.widgets
-        x1 = widgets['x1']
-        x1.input_css_class = 'col-md-2'
-        x1.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
-        alsoProvides(x1, IObjectData)
-        x2 = widgets['x2']
-        x2.input_css_class = 'col-md-2'
-        x2.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
-        alsoProvides(x2, IObjectData)
-        y1 = widgets['y1']
-        y1.input_css_class = 'col-md-2'
-        y1.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
-        alsoProvides(y1, IObjectData)
-        y2 = widgets['y2']
-        y2.input_css_class = 'col-md-2'
-        y2.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
-        alsoProvides(y2, IObjectData)
-        projection = widgets['projection']
-        projection.object_data = {
-            'ams-events-handlers': {
-                'change.select2': 'PyAMS_GIS.area.changedProjection'
-            }
-        }
-        alsoProvides(projection, IObjectData)
-
-    @property
-    def wgs_coordinates(self):
-        value = self.field.get(self.field.interface(self.context))
-        if not value:
-            return json.dumps({'x1': None,
-                               'y1': None,
-                               'x2': None,
-                               'y2': None})
-        else:
-            point1, point2 = value.wgs_coordinates
-            return json.dumps({'x1': float(point1[0]),
-                               'y1': float(point1[1]),
-                               'x2': float(point2[0]),
-                               'y2': float(point2[1])})
-
-
-@adapter_config(context=(IGeoAreaField, IFormLayer), provides=IFieldWidget)
-def GeoAreaFieldWidget(field, request):
-    """GeoArea field widget factory"""
-    return FieldWidget(field, GeoAreaWidget(request))
--- a/src/pyams_gis/widget/point.py	Wed Jul 11 11:39:49 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-#
-# Copyright (c) 2008-2017 Thierry Florac <tflorac AT ulthar.net>
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-
-__docformat__ = 'restructuredtext'
-
-
-# import standard library
-import json
-
-# import interfaces
-from pyams_gis.interfaces import IGeoPointZ
-from pyams_form.interfaces.form import IFormLayer, IForm
-from pyams_gis.interfaces.widget import IGeoPointWidget, IGeoPointZWidget
-from pyams_gis.schema import IGeoPoint, IGeoPointField, IGeoPointZField
-from pyams_utils.interfaces.data import IObjectData
-from z3c.form.interfaces import IFieldWidget, IObjectFactory
-
-# import packages
-from pyams_form.widget import widgettemplate_config
-from pyams_gis.point import GeoPoint, GeoPointZ
-from pyams_utils.adapter import adapter_config
-from z3c.form.browser.object import ObjectWidget
-from z3c.form.object import getIfName
-from z3c.form.widget import FieldWidget
-from zope.interface import implementer_only, alsoProvides, Interface
-
-
-@adapter_config(name=getIfName(IGeoPoint),
-                context=(Interface, IFormLayer, IForm, IGeoPointWidget), provides=IObjectFactory)
-class GeoPointObjectFactory(object):
-    """GeoPointZ object factory"""
-
-    def __init__(self, context, request, form, widget):
-        self.context = context
-        self.request = request
-        self.form = form
-        self.widget = widget
-
-    def __call__(self, data):
-        return GeoPoint()
-
-
-@widgettemplate_config(mode='input', template='templates/geopoint-input.pt', layer=IFormLayer)
-@implementer_only(IGeoPointWidget)
-class GeoPointWidget(ObjectWidget):
-    """GeoPoint widget"""
-
-    def updateWidgets(self, setErrors=True):
-        super(GeoPointWidget, self).updateWidgets(setErrors)
-        widgets = self.subform.widgets
-        longitude = widgets['longitude']
-        longitude.label_css_class = 'control-label col-md-3'
-        longitude.input_css_class = 'col-md-2'
-        longitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
-        alsoProvides(longitude, IObjectData)
-        latitude = widgets['latitude']
-        latitude.label_css_class = 'control-label col-md-3'
-        latitude.input_css_class = 'col-md-2'
-        latitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
-        alsoProvides(latitude, IObjectData)
-        projection = widgets['projection']
-        projection.label_css_class = 'control-label col-md-3'
-        projection.input_css_class = 'col-md-9'
-        projection.object_data = {'ams-events-handlers': {'change.select2': 'PyAMS_GIS.position.changedProjection'}}
-        alsoProvides(projection, IObjectData)
-
-    @property
-    def wgs_coordinates(self):
-        value = self.field.get(self.field.interface(self.context))
-        if not value:
-            return json.dumps({'longitude': None,
-                               'latitude': None})
-        else:
-            point = value.wgs_coordinates
-            return json.dumps({'longitude': float(point[0]),
-                               'latitude': float(point[1])})
-
-
-@adapter_config(context=(IGeoPointField, IFormLayer), provides=IFieldWidget)
-def GeoPointFieldWidget(field, request):
-    """GeoPoint field widget factory"""
-    return FieldWidget(field, GeoPointWidget(request))
-
-
-@adapter_config(name=getIfName(IGeoPointZ),
-                context=(Interface, IFormLayer, IForm, IGeoPointZWidget), provides=IObjectFactory)
-class GeoPointZObjectFactory(object):
-    """GeoPointZ object factory"""
-
-    def __init__(self, context, request, form, widget):
-        self.context = context
-        self.request = request
-        self.form = form
-        self.widget = widget
-
-    def __call__(self, data):
-        return GeoPointZ()
-
-
-@widgettemplate_config(mode='input', template='templates/geopoint-input.pt', layer=IFormLayer)
-@implementer_only(IGeoPointZWidget)
-class GeoPointZWidget(ObjectWidget):
-    """GeoPointZ widget"""
-
-    def updateWidgets(self, setErrors=True):
-        super(GeoPointZWidget, self).updateWidgets(setErrors)
-        widgets = self.subform.widgets
-        longitude = widgets['longitude']
-        longitude.label_css_class = 'control-label col-md-3'
-        longitude.input_css_class = 'col-md-2'
-        longitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
-        alsoProvides(longitude, IObjectData)
-        latitude = widgets['latitude']
-        latitude.label_css_class = 'control-label col-md-3'
-        latitude.input_css_class = 'col-md-2'
-        latitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
-        alsoProvides(latitude, IObjectData)
-        projection = widgets['projection']
-        projection.label_css_class = 'control-label col-md-3'
-        latitude.input_css_class = 'col-md-9'
-        projection.object_data = {'ams-events-handlers': {'change.select2': 'PyAMS_GIS.position.changedProjection'}}
-        alsoProvides(projection, IObjectData)
-        altitude = widgets['altitude']
-        altitude.label_css_class = 'control-label col-md-3'
-        altitude.input_css_class = 'col-md-2'
-
-    @property
-    def wgs_coordinates(self):
-        value = self.field.get(self.field.interface(self.context))
-        if not value:
-            return json.dumps({'longitude': None,
-                               'latitude': None})
-        else:
-            point = value.wgs_coordinates
-            return json.dumps({'longitude': float(point[0]),
-                               'latitude': float(point[1])})
-
-
-@adapter_config(context=(IGeoPointZField, IFormLayer), provides=IFieldWidget)
-def GeoPointZFieldWidget(field, request):
-    """GeoPointZ field widget factory"""
-    return FieldWidget(field, GeoPointZWidget(request))
--- a/src/pyams_gis/widget/templates/geoarea-input.pt	Wed Jul 11 11:39:49 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-<div class="object-widget" tal:attributes="class view/klass" i18n:domain="pyams_gis">
-	<fieldset
-		tal:define="name python:view.name.replace('.','_')"
-		data-ams-plugins="pyams_gis"
-		tal:attributes="class view/fieldset_class | default;
-						data-ams-plugin-pyams_gis-src tales:resource_path('pyams_gis:pyams_gis')"
-		data-ams-plugin-pyams_gis-async="false">
-		<div class="col-md-6 pull-right">
-			<div class="btn btn-default bg-color-silverLight"
-				 tal:attributes="href string:#modal_dialog_${name}" data-toggle="modal">
-				<i class="fa fa-fw fa-lg fa-map-marker hint opaque align-base"
-				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
-				   title="Select area from map" i18n:attributes="title"></i>
-			</div>
-			<div class="btn btn-default bg-color-silverLight"
-				data-ams-click-handler="PyAMS_GIS.area.clear">
-				<i class="fa fa-fw fa-lg fa-trash hint opaque align-base"
-				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
-				   title="Remove area settings" i18n:attributes="title"></i>
-			</div>
-			<div id="modal_dialog" class="modal fade"
-				 tal:attributes="id string:modal_dialog_${name}"
-				 data-ams-events-handlers='{"show.bs.modal": "PyAMS_GIS.area.init"}'>
-				<div class="modal-dialog modal-max">
-					<div class="modal-content">
-						<div class="modal-header">
-							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
-								<i class="fa fa-fw fa-times-circle"></i>
-							</button>
-							<h3 class="modal-title">
-								<span class="title" i18n:translate="">Select map area</span>
-							</h3>
-						</div>
-						<div class="ams-form">
-							<div class="modal-body">
-								<div class="map-header"
-									 tal:define="header provider:pyams_gis.map.header"
-									 tal:condition="header"
-									 tal:content="structure header"></div>
-								<div class="map map-area" id="map_area" style="width: 100%; height: 600px;"
-									 data-map-leaflet-fieldname="fieldname"
-									 tal:attributes="id string:map_area_${name};
-													 data-map-leaflet-fieldname view/name;"></div>
-							</div>
-							<footer>
-								<button type="button" class="btn btn-primary close-widget" data-dismiss="modal">OK</button>
-							</footer>
-						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-		<div class="clearfix-xs clearfix-sm"></div>
-		<tal:loop repeat="widget view/subform/widgets/values">
-			<input type="hidden"
-				   tal:condition="python:widget.mode == 'hidden'"
-				   tal:replace="structure widget/render" />
-			<tal:if condition="python:widget.mode != 'hidden'">
-				<div tal:define="required python:'required-field' if widget.required and (widget.mode != 'display') else ''"
-					 tal:attributes="class string:form-group ${required}">
-					<label class="control-label col-md-3"
-						   tal:attributes="class widget/label_css_class | group/label_css_class | view/label_css_class | default">
-						<span>
-							<tal:var content="python:request.localizer.translate(widget.label)" />
-							<i class="fa fa-question-circle hint" title="Input hint"
-							   tal:define="description python:getattr(widget, 'description', widget.field.description)"
-							   tal:condition="description"
-							   tal:attributes="title description;
-											   data-ams-hint-html python:'<' in description;"></i>
-						</span>
-					</label>
-					<div class="col-md-9"
-						 tal:attributes="class widget/input_css_class | group/input_css_class | view/input_css_class | default">
-						<label class="input"
-							   tal:attributes="class widget/widget_css_class | default;">
-							<input tal:replace="structure widget/render" />
-						</label>
-					</div>
-				</div>
-			</tal:if>
-		</tal:loop>
-		<input name="field-empty-marker" type="hidden" value="1"
-			   tal:attributes="name string:${view/name}-empty-marker" />
-	</fieldset>
-</div>
--- a/src/pyams_gis/widget/templates/geopoint-input.pt	Wed Jul 11 11:39:49 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-<div class="object-widget" tal:attributes="class view/klass" i18n:domain="pyams_gis">
-	<fieldset
-		tal:define="name python:view.name.replace('.','_')"
-		data-ams-plugins="pyams_gis"
-		tal:attributes="class view/fieldset_class | default;
-						data-ams-plugin-pyams_gis-src tales:resource_path('pyams_gis:pyams_gis');"
-		data-ams-plugin-pyams_gis-async="false">
-		<div class="col-md-6 pull-right">
-			<div class="btn btn-default bg-color-silverLight"
-				 tal:attributes="href string:#modal_dialog_${name}" data-toggle="modal">
-				<i class="fa fa-fw fa-lg fa-map-marker hint opaque align-base"
-				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
-				   title="Select location from map" i18n:attributes="title"></i>
-			</div>
-			<div class="btn btn-default bg-color-silverLight"
-				data-ams-click-handler="PyAMS_GIS.position.clear">
-				<i class="fa fa-fw fa-lg fa-trash hint opaque align-base"
-				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
-				   title="Remove position settings" i18n:attributes="title"></i>
-			</div>
-			<div id="modal_dialog" class="modal fade"
-				 tal:attributes="id string:modal_dialog_${name}"
-				 data-ams-events-handlers='{"show.bs.modal": "PyAMS_GIS.position.init"}'>
-				<div class="modal-dialog modal-max">
-					<div class="modal-content">
-						<div class="modal-header">
-							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
-								<i class="fa fa-fw fa-times-circle"></i>
-							</button>
-							<h3 class="modal-title">
-								<span class="title" i18n:translate="">Select marker position</span>
-							</h3>
-						</div>
-						<div class="ams-form">
-							<div class="modal-body">
-								<div class="map-header"
-									 tal:define="header provider:pyams_gis.map.header"
-									 tal:condition="header"
-									 tal:content="structure header"></div>
-								<div class="map map-location" id="map_location" style="width: 100%; height: 600px;"
-									 data-map-leaflet-fieldname="fieldname"
-									 tal:attributes="id string:map_location_${name};
-													 data-map-leaflet-fieldname view/name;"></div>
-							</div>
-							<footer>
-								<button type="button" class="btn btn-primary close-widget"
-										data-dismiss="modal" data-ams-click-event="marker.closed.position"
-										tal:attributes='data-ams-click-event-options string:{"fieldname": "${view/name}"}'>OK</button>
-							</footer>
-						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-		<div class="clearfix-xs clearfix-sm"></div>
-		<tal:loop repeat="widget view/subform/widgets/values">
-			<input type="hidden"
-				   tal:condition="python:widget.mode == 'hidden'"
-				   tal:replace="structure widget/render" />
-			<tal:if condition="python:widget.mode != 'hidden'">
-				<div tal:define="required python:'required-field' if widget.required and (widget.mode != 'display') else ''"
-					 tal:attributes="class string:form-group ${required}">
-					<label class="control-label col-md-3"
-						   tal:attributes="class widget/label_css_class | group/label_css_class | view/label_css_class | default">
-						<span>
-							<tal:var content="python:request.localizer.translate(widget.label)" />
-							<i class="fa fa-question-circle hint" title="Input hint"
-							   tal:define="description python:getattr(widget, 'description', widget.field.description)"
-							   tal:condition="description"
-							   tal:attributes="title description;
-											   data-ams-hint-html python:'<' in description;"></i>
-						</span>
-					</label>
-					<div class="col-md-9"
-						 tal:attributes="class widget/input_css_class | group/input_css_class | view/input_css_class | default">
-						<label class="input"
-							   tal:attributes="class widget/widget_css_class | default;">
-							<input tal:replace="structure widget/render" />
-						</label>
-					</div>
-				</div>
-			</tal:if>
-		</tal:loop>
-		<input name="field-empty-marker" type="hidden" value="1"
-			   tal:attributes="name string:${view/name}-empty-marker" />
-	</fieldset>
-</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_gis/zmi/widget/__init__.py	Wed Jul 11 11:40:09 2018 +0200
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2008-2017 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+
+# import packages
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_gis/zmi/widget/area.py	Wed Jul 11 11:40:09 2018 +0200
@@ -0,0 +1,103 @@
+#
+# Copyright (c) 2008-2017 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+import json
+
+# import interfaces
+from pyams_form.interfaces.form import IFormLayer, IForm
+from pyams_gis.interfaces import IGeoArea
+from pyams_gis.interfaces.widget import IGeoAreaWidget
+from pyams_gis.schema import IGeoAreaField
+from pyams_utils.interfaces.data import IObjectData
+from z3c.form.interfaces import IFieldWidget, IObjectFactory
+
+# import packages
+from pyams_form.widget import widgettemplate_config
+from pyams_gis.area import GeoArea
+from pyams_utils.adapter import adapter_config
+from z3c.form.browser.object import ObjectWidget
+from z3c.form.object import getIfName
+from z3c.form.widget import FieldWidget
+from zope.interface import implementer_only, alsoProvides, Interface
+
+
+@adapter_config(name=getIfName(IGeoArea),
+                context=(Interface, IFormLayer, IForm, IGeoAreaWidget), provides=IObjectFactory)
+class GeoAreaObjectFactory(object):
+    """GeoArea object factory"""
+
+    def __init__(self, context, request, form, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.widget = widget
+
+    def __call__(self, data):
+        return GeoArea()
+
+
+@widgettemplate_config(mode='input', template='templates/geoarea-input.pt', layer=IFormLayer)
+@implementer_only(IGeoAreaWidget)
+class GeoAreaWidget(ObjectWidget):
+    """GeoArea widget"""
+
+    def updateWidgets(self, setErrors=True):
+        super(GeoAreaWidget, self).updateWidgets()
+        widgets = self.subform.widgets
+        x1 = widgets['x1']
+        x1.input_css_class = 'col-md-2'
+        x1.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
+        alsoProvides(x1, IObjectData)
+        x2 = widgets['x2']
+        x2.input_css_class = 'col-md-2'
+        x2.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
+        alsoProvides(x2, IObjectData)
+        y1 = widgets['y1']
+        y1.input_css_class = 'col-md-2'
+        y1.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
+        alsoProvides(y1, IObjectData)
+        y2 = widgets['y2']
+        y2.input_css_class = 'col-md-2'
+        y2.object_data = {'ams-change-handler': 'PyAMS_GIS.area.changedCoordinate'}
+        alsoProvides(y2, IObjectData)
+        projection = widgets['projection']
+        projection.object_data = {
+            'ams-events-handlers': {
+                'change.select2': 'PyAMS_GIS.area.changedProjection'
+            }
+        }
+        alsoProvides(projection, IObjectData)
+
+    @property
+    def wgs_coordinates(self):
+        value = self.field.get(self.field.interface(self.context))
+        if not value:
+            return json.dumps({'x1': None,
+                               'y1': None,
+                               'x2': None,
+                               'y2': None})
+        else:
+            point1, point2 = value.wgs_coordinates
+            return json.dumps({'x1': float(point1[0]),
+                               'y1': float(point1[1]),
+                               'x2': float(point2[0]),
+                               'y2': float(point2[1])})
+
+
+@adapter_config(context=(IGeoAreaField, IFormLayer), provides=IFieldWidget)
+def GeoAreaFieldWidget(field, request):
+    """GeoArea field widget factory"""
+    return FieldWidget(field, GeoAreaWidget(request))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_gis/zmi/widget/point.py	Wed Jul 11 11:40:09 2018 +0200
@@ -0,0 +1,151 @@
+#
+# Copyright (c) 2008-2017 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+import json
+
+# import interfaces
+from pyams_gis.interfaces import IGeoPointZ
+from pyams_form.interfaces.form import IFormLayer, IForm
+from pyams_gis.interfaces.widget import IGeoPointWidget, IGeoPointZWidget
+from pyams_gis.schema import IGeoPoint, IGeoPointField, IGeoPointZField
+from pyams_utils.interfaces.data import IObjectData
+from z3c.form.interfaces import IFieldWidget, IObjectFactory
+
+# import packages
+from pyams_form.widget import widgettemplate_config
+from pyams_gis.point import GeoPoint, GeoPointZ
+from pyams_utils.adapter import adapter_config
+from z3c.form.browser.object import ObjectWidget
+from z3c.form.object import getIfName
+from z3c.form.widget import FieldWidget
+from zope.interface import implementer_only, alsoProvides, Interface
+
+
+@adapter_config(name=getIfName(IGeoPoint),
+                context=(Interface, IFormLayer, IForm, IGeoPointWidget), provides=IObjectFactory)
+class GeoPointObjectFactory(object):
+    """GeoPointZ object factory"""
+
+    def __init__(self, context, request, form, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.widget = widget
+
+    def __call__(self, data):
+        return GeoPoint()
+
+
+@widgettemplate_config(mode='input', template='templates/geopoint-input.pt', layer=IFormLayer)
+@implementer_only(IGeoPointWidget)
+class GeoPointWidget(ObjectWidget):
+    """GeoPoint widget"""
+
+    def updateWidgets(self, setErrors=True):
+        super(GeoPointWidget, self).updateWidgets(setErrors)
+        widgets = self.subform.widgets
+        longitude = widgets['longitude']
+        longitude.label_css_class = 'control-label col-md-3'
+        longitude.input_css_class = 'col-md-2'
+        longitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
+        alsoProvides(longitude, IObjectData)
+        latitude = widgets['latitude']
+        latitude.label_css_class = 'control-label col-md-3'
+        latitude.input_css_class = 'col-md-2'
+        latitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
+        alsoProvides(latitude, IObjectData)
+        projection = widgets['projection']
+        projection.label_css_class = 'control-label col-md-3'
+        projection.input_css_class = 'col-md-9'
+        projection.object_data = {'ams-events-handlers': {'change.select2': 'PyAMS_GIS.position.changedProjection'}}
+        alsoProvides(projection, IObjectData)
+
+    @property
+    def wgs_coordinates(self):
+        value = self.field.get(self.field.interface(self.context))
+        if not value:
+            return json.dumps({'longitude': None,
+                               'latitude': None})
+        else:
+            point = value.wgs_coordinates
+            return json.dumps({'longitude': float(point[0]),
+                               'latitude': float(point[1])})
+
+
+@adapter_config(context=(IGeoPointField, IFormLayer), provides=IFieldWidget)
+def GeoPointFieldWidget(field, request):
+    """GeoPoint field widget factory"""
+    return FieldWidget(field, GeoPointWidget(request))
+
+
+@adapter_config(name=getIfName(IGeoPointZ),
+                context=(Interface, IFormLayer, IForm, IGeoPointZWidget), provides=IObjectFactory)
+class GeoPointZObjectFactory(object):
+    """GeoPointZ object factory"""
+
+    def __init__(self, context, request, form, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.widget = widget
+
+    def __call__(self, data):
+        return GeoPointZ()
+
+
+@widgettemplate_config(mode='input', template='templates/geopoint-input.pt', layer=IFormLayer)
+@implementer_only(IGeoPointZWidget)
+class GeoPointZWidget(ObjectWidget):
+    """GeoPointZ widget"""
+
+    def updateWidgets(self, setErrors=True):
+        super(GeoPointZWidget, self).updateWidgets(setErrors)
+        widgets = self.subform.widgets
+        longitude = widgets['longitude']
+        longitude.label_css_class = 'control-label col-md-3'
+        longitude.input_css_class = 'col-md-2'
+        longitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
+        alsoProvides(longitude, IObjectData)
+        latitude = widgets['latitude']
+        latitude.label_css_class = 'control-label col-md-3'
+        latitude.input_css_class = 'col-md-2'
+        latitude.object_data = {'ams-change-handler': 'PyAMS_GIS.position.changedCoordinate'}
+        alsoProvides(latitude, IObjectData)
+        projection = widgets['projection']
+        projection.label_css_class = 'control-label col-md-3'
+        latitude.input_css_class = 'col-md-9'
+        projection.object_data = {'ams-events-handlers': {'change.select2': 'PyAMS_GIS.position.changedProjection'}}
+        alsoProvides(projection, IObjectData)
+        altitude = widgets['altitude']
+        altitude.label_css_class = 'control-label col-md-3'
+        altitude.input_css_class = 'col-md-2'
+
+    @property
+    def wgs_coordinates(self):
+        value = self.field.get(self.field.interface(self.context))
+        if not value:
+            return json.dumps({'longitude': None,
+                               'latitude': None})
+        else:
+            point = value.wgs_coordinates
+            return json.dumps({'longitude': float(point[0]),
+                               'latitude': float(point[1])})
+
+
+@adapter_config(context=(IGeoPointZField, IFormLayer), provides=IFieldWidget)
+def GeoPointZFieldWidget(field, request):
+    """GeoPointZ field widget factory"""
+    return FieldWidget(field, GeoPointZWidget(request))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_gis/zmi/widget/templates/geoarea-input.pt	Wed Jul 11 11:40:09 2018 +0200
@@ -0,0 +1,85 @@
+<div class="object-widget" tal:attributes="class view/klass" i18n:domain="pyams_gis">
+	<fieldset
+		tal:define="name python:view.name.replace('.','_')"
+		data-ams-plugins="pyams_gis"
+		tal:attributes="class view/fieldset_class | default;
+						data-ams-plugin-pyams_gis-src tales:resource_path('pyams_gis:pyams_gis')"
+		data-ams-plugin-pyams_gis-async="false">
+		<div class="col-md-6 pull-right">
+			<div class="btn btn-default bg-color-silverLight"
+				 tal:attributes="href string:#modal_dialog_${name}" data-toggle="modal">
+				<i class="fa fa-fw fa-lg fa-map-marker hint opaque align-base"
+				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
+				   title="Select area from map" i18n:attributes="title"></i>
+			</div>
+			<div class="btn btn-default bg-color-silverLight"
+				data-ams-click-handler="PyAMS_GIS.area.clear">
+				<i class="fa fa-fw fa-lg fa-trash hint opaque align-base"
+				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
+				   title="Remove area settings" i18n:attributes="title"></i>
+			</div>
+			<div id="modal_dialog" class="modal fade"
+				 tal:attributes="id string:modal_dialog_${name}"
+				 data-ams-events-handlers='{"show.bs.modal": "PyAMS_GIS.area.init"}'>
+				<div class="modal-dialog modal-max">
+					<div class="modal-content">
+						<div class="modal-header">
+							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
+								<i class="fa fa-fw fa-times-circle"></i>
+							</button>
+							<h3 class="modal-title">
+								<span class="title" i18n:translate="">Select map area</span>
+							</h3>
+						</div>
+						<div class="ams-form">
+							<div class="modal-body">
+								<div class="map-header"
+									 tal:define="header provider:pyams_gis.map.header"
+									 tal:condition="header"
+									 tal:content="structure header"></div>
+								<div class="map map-area" id="map_area" style="width: 100%; height: 600px;"
+									 data-map-leaflet-fieldname="fieldname"
+									 tal:attributes="id string:map_area_${name};
+													 data-map-leaflet-fieldname view/name;"></div>
+							</div>
+							<footer>
+								<button type="button" class="btn btn-primary close-widget" data-dismiss="modal">OK</button>
+							</footer>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<div class="clearfix-xs clearfix-sm"></div>
+		<tal:loop repeat="widget view/subform/widgets/values">
+			<input type="hidden"
+				   tal:condition="python:widget.mode == 'hidden'"
+				   tal:replace="structure widget/render" />
+			<tal:if condition="python:widget.mode != 'hidden'">
+				<div tal:define="required python:'required-field' if widget.required and (widget.mode != 'display') else ''"
+					 tal:attributes="class string:form-group ${required}">
+					<label class="control-label col-md-3"
+						   tal:attributes="class widget/label_css_class | group/label_css_class | view/label_css_class | default">
+						<span>
+							<tal:var content="python:request.localizer.translate(widget.label)" />
+							<i class="fa fa-question-circle hint" title="Input hint"
+							   tal:define="description python:getattr(widget, 'description', widget.field.description)"
+							   tal:condition="description"
+							   tal:attributes="title description;
+											   data-ams-hint-html python:'<' in description;"></i>
+						</span>
+					</label>
+					<div class="col-md-9"
+						 tal:attributes="class widget/input_css_class | group/input_css_class | view/input_css_class | default">
+						<label class="input"
+							   tal:attributes="class widget/widget_css_class | default;">
+							<input tal:replace="structure widget/render" />
+						</label>
+					</div>
+				</div>
+			</tal:if>
+		</tal:loop>
+		<input name="field-empty-marker" type="hidden" value="1"
+			   tal:attributes="name string:${view/name}-empty-marker" />
+	</fieldset>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_gis/zmi/widget/templates/geopoint-input.pt	Wed Jul 11 11:40:09 2018 +0200
@@ -0,0 +1,87 @@
+<div class="object-widget" tal:attributes="class view/klass" i18n:domain="pyams_gis">
+	<fieldset
+		tal:define="name python:view.name.replace('.','_')"
+		data-ams-plugins="pyams_gis"
+		tal:attributes="class view/fieldset_class | default;
+						data-ams-plugin-pyams_gis-src tales:resource_path('pyams_gis:pyams_gis');"
+		data-ams-plugin-pyams_gis-async="false">
+		<div class="col-md-6 pull-right">
+			<div class="btn btn-default bg-color-silverLight"
+				 tal:attributes="href string:#modal_dialog_${name}" data-toggle="modal">
+				<i class="fa fa-fw fa-lg fa-map-marker hint opaque align-base"
+				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
+				   title="Select location from map" i18n:attributes="title"></i>
+			</div>
+			<div class="btn btn-default bg-color-silverLight"
+				data-ams-click-handler="PyAMS_GIS.position.clear">
+				<i class="fa fa-fw fa-lg fa-trash hint opaque align-base"
+				   data-ams-hint-gravity="se" data-ams-hint-offset="10"
+				   title="Remove position settings" i18n:attributes="title"></i>
+			</div>
+			<div id="modal_dialog" class="modal fade"
+				 tal:attributes="id string:modal_dialog_${name}"
+				 data-ams-events-handlers='{"show.bs.modal": "PyAMS_GIS.position.init"}'>
+				<div class="modal-dialog modal-max">
+					<div class="modal-content">
+						<div class="modal-header">
+							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
+								<i class="fa fa-fw fa-times-circle"></i>
+							</button>
+							<h3 class="modal-title">
+								<span class="title" i18n:translate="">Select marker position</span>
+							</h3>
+						</div>
+						<div class="ams-form">
+							<div class="modal-body">
+								<div class="map-header"
+									 tal:define="header provider:pyams_gis.map.header"
+									 tal:condition="header"
+									 tal:content="structure header"></div>
+								<div class="map map-location" id="map_location" style="width: 100%; height: 600px;"
+									 data-map-leaflet-fieldname="fieldname"
+									 tal:attributes="id string:map_location_${name};
+													 data-map-leaflet-fieldname view/name;"></div>
+							</div>
+							<footer>
+								<button type="button" class="btn btn-primary close-widget"
+										data-dismiss="modal" data-ams-click-event="marker.closed.position"
+										tal:attributes='data-ams-click-event-options string:{"fieldname": "${view/name}"}'>OK</button>
+							</footer>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<div class="clearfix-xs clearfix-sm"></div>
+		<tal:loop repeat="widget view/subform/widgets/values">
+			<input type="hidden"
+				   tal:condition="python:widget.mode == 'hidden'"
+				   tal:replace="structure widget/render" />
+			<tal:if condition="python:widget.mode != 'hidden'">
+				<div tal:define="required python:'required-field' if widget.required and (widget.mode != 'display') else ''"
+					 tal:attributes="class string:form-group ${required}">
+					<label class="control-label col-md-3"
+						   tal:attributes="class widget/label_css_class | group/label_css_class | view/label_css_class | default">
+						<span>
+							<tal:var content="python:request.localizer.translate(widget.label)" />
+							<i class="fa fa-question-circle hint" title="Input hint"
+							   tal:define="description python:getattr(widget, 'description', widget.field.description)"
+							   tal:condition="description"
+							   tal:attributes="title description;
+											   data-ams-hint-html python:'<' in description;"></i>
+						</span>
+					</label>
+					<div class="col-md-9"
+						 tal:attributes="class widget/input_css_class | group/input_css_class | view/input_css_class | default">
+						<label class="input"
+							   tal:attributes="class widget/widget_css_class | default;">
+							<input tal:replace="structure widget/render" />
+						</label>
+					</div>
+				</div>
+			</tal:if>
+		</tal:loop>
+		<input name="field-empty-marker" type="hidden" value="1"
+			   tal:attributes="name string:${view/name}-empty-marker" />
+	</fieldset>
+</div>