diff --git a/docs/mra_reference.rst b/docs/mra_reference.rst index fd049f5db67d57a00bc4e6b94e8f4c31a3cb7fcb..f8b856238ac00fa39164e47550b4c15d3a688cc2 100644 --- a/docs/mra_reference.rst +++ b/docs/mra_reference.rst @@ -72,12 +72,6 @@ See `Mapfile specification`_ for more information about the Mapfile. Operations ---------- -For current version of MapServer Rest API, manipulate a ``mapfile`` is not implemented. -And so, you can't create, modify or delete a mapfile. -Please use the `mapfile model`_ and just give us some time to implement this operations. - -.. _Mapfile model: https://github.com/neogeo-technologies/mra/blob/develop/docs/model.map - ``/maps.<format>`` ^^^^^^^^^^^^^^^^^^ @@ -88,7 +82,9 @@ Please use the `mapfile model`_ and just give us some time to implement this ope +========+=================================+=============+====================+ | GET | List all mapfiles | 200 | XML, JSON, HTML | +--------+---------------------------------+-------------+--------------------+ -| POST | *TODO: Create a new mapfile* | | | +| POST | Create a new mapfile | 201 with | XML, JSON | +| | | ``location``| | +| | | header | | +--------+---------------------------------+-------------+--------------------+ | PUT | | 405 | | +--------+---------------------------------+-------------+--------------------+ @@ -98,7 +94,7 @@ Please use the `mapfile model`_ and just give us some time to implement this ope *Exceptions:* -* *TODO: POST for a mapfile already exist: 409 Conflict* +* POST for a mapfile already exist: 409 Conflict ``/maps/<map>.<format>`` @@ -116,7 +112,7 @@ Please use the `mapfile model`_ and just give us some time to implement this ope +--------+---------------------------------+-------------+--------------------+ | PUT | *TODO: Modify mapfile <map>* | | | +--------+---------------------------------+-------------+--------------------+ -| DELETE | *TODO: Delete mapfile <map>* | | | +| DELETE | Delete mapfile <map> | | | +--------+---------------------------------+-------------+--------------------+ *Exceptions:* diff --git a/src/mapfile.py b/src/mapfile.py index 34e98247b65d42ee8a6b2b33dfb6e2a8d0af30e6..b9665ddda868d8c0f9fbfca6dcfdbc63c1a10d00 100644 --- a/src/mapfile.py +++ b/src/mapfile.py @@ -721,15 +721,22 @@ class Mapfile(MetadataMixin): # We have one workspace that represents the file. self.__default_workspace = MapfileWorkspace(self) - def save(self, path=None): if path is None: path = self.path self.ms.save(path) + def delete(self, path=None): + if path is None: + path = self.path + os.remove(path) + def rawtext(self): open(self.path, "r").read() + def update(self, configuration): + raise NotImplemented() + # Layers: def iter_ms_layers(self, attr={}, meta={}, mra={}): diff --git a/src/maptools.py b/src/maptools.py index 3ab59418a424b64299cc71324491776274da64f0..fb18c58bd29ced8fb5a074e78bb737dbfd93015b 100644 --- a/src/maptools.py +++ b/src/maptools.py @@ -24,7 +24,96 @@ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +from webapp import KeyExists +import metadata + import mapscript +import os.path + +def create_mapfile(path, map_name, data): + if os.path.exists("%s.map" % path): + raise KeyExists(map_name) + + mf = mapscript.mapObj() + mf.name = map_name + + # The following could be defined in <mapfile.py>: + + mf.web.metadata.set("ows_name", map_name) + mf.web.metadata.set("ows_title", data.get("title", map_name)) + mf.web.metadata.set("ows_abstract", data.get("abstract", "")) + + # Set default values: + # It should be configurable to the future. + + # mf.web.metadata.set("ows_keywordlist", "") + # mf.web.metadata.set("ows_keywordlist_vocabulary", "") + # + ows_keywordlist_[vocabulary’s name]_items + # mf.web.metadata.set("wms_onlineresource", "") + # mf.web.metadata.set("wfs_onlineresource", "") + # mf.web.metadata.set("wms_service_onlineresource", "") + # mf.web.metadata.set("wfs_service_onlineresource", "") + mf.web.metadata.set("wms_srs", "EPSG:4326") + mf.web.metadata.set("wfs_srs", "EPSG:4326") + mf.web.metadata.set("wms_bbox_extended", "true") + # mf.web.metadata.set("wms_resx", "") + # mf.web.metadata.set("wms_resy", "") + mf.web.metadata.set("wms_enable_request", "GetCapabilities !GetMap !GetFeatureInfo !GetLegendGraphic") + mf.web.metadata.set("wfs_enable_request", "GetCapabilities !DescribeFeatureType !GetFeature") + mf.web.metadata.set("ows_sld_enabled", "true") + # mf.web.metadata.set("ows_schemas_location", "") + # mf.web.metadata.set("ows_updatesequence", "") + # mf.web.metadata.set("ows_addresstype", "") + # mf.web.metadata.set("ows_address", "") + # mf.web.metadata.set("ows_city", "") + # mf.web.metadata.set("ows_stateorprovince", "") + # mf.web.metadata.set("ows_postcode", "") + # mf.web.metadata.set("ows_contactperson", "") + # mf.web.metadata.set("ows_contactposition", "") + # mf.web.metadata.set("ows_contactorganization", "") + # mf.web.metadata.set("ows_contactelectronicmailaddress", "") + # mf.web.metadata.set("ows_contactfacsimiletelephone", "") + # mf.web.metadata.set("ows_contactvoicetelephone, "")" + # mf.web.metadata.set("wms_fees", "") + # mf.web.metadata.set("wfs_fees", "") + # mf.web.metadata.set("wms_accessconstraints", "") + # mf.web.metadata.set("wfs_accessconstraints", "") + # mf.web.metadata.set("ows_attribution_logourl_format", "") + # mf.web.metadata.set("ows_attribution_logourl_height", "") + # mf.web.metadata.set("ows_attribution_logourl_href", "") + # mf.web.metadata.set("ows_attribution_logourl_width", "") + # mf.web.metadata.set("ows_attribution_onlineresource", "") + # mf.web.metadata.set("ows_attribution_title", "") + mf.web.metadata.set("wms_encoding", "UTF-8") + mf.web.metadata.set("wfs_encoding", "UTF-8") + # mf.web.metadata.set("wms_getcapabilities_version", "") + # mf.web.metadata.set("wfs_getcapabilities_version", "") + # mf.web.metadata.set("wms_getmap_formatlist", "") + # mf.web.metadata.set("wms_getlegendgraphic_formatlist", "") + # mf.web.metadata.set("wms_feature_info_mime_type", "") + # mf.web.metadata.set("wms_timeformat", "") + # mf.web.metadata.set("wms_languages", "") + # mf.web.metadata.set("wms_layerlimit", "") + # mf.web.metadata.set("wms_rootlayer_abstract", "") + # mf.web.metadata.set("wms_rootlayer_keywordlist", "") + # mf.web.metadata.set("wms_rootlayer_title", "") + # mf.web.metadata.set("wfs_maxfeatures", "") + # mf.web.metadata.set("wfs_feature_collection", "") + # mf.web.metadata.set("wfs_namespace_uri", "") + # mf.web.metadata.set("wfs_namespace_prefix", "") + # ... + + mf.status = mapscript.MS_ON + mf.setSize(256,256) + mf.maxsize = 4096 + mf.resolution = 96 + mf.imagetype = 'png' + mf.imagecolor.setRGB(255,255,255) + mf.setProjection("init=epsg:4326") + mf.setExtent(-180,-90,180,90) + mf.units = mapscript.MS_DD + + mf.save("%s.map" % path) def create_def_polygon_class(layer, s_name='default_polygon'): layer.classgroup = s_name diff --git a/src/server.py b/src/server.py index 33388990818ce75f5c9034922c9ff72f47f08c53..ab31b3887d67da0bfe6ae99754ec4cba330047f9 100755 --- a/src/server.py +++ b/src/server.py @@ -37,6 +37,7 @@ import webapp from webapp import HTTPCompatible, urlmap, get_data import tools +import maptools from tools import get_mapfile, get_mapfile_workspace, get_config, href from pyxml import Entries @@ -87,11 +88,15 @@ class mapfiles(object): return {"mapfiles": mapfiles} @HTTPCompatible() - def POST(self, map_name, format): - data = get_data() + def POST(self, format): + data = get_data(name="mapfile", mandatory=["name"], authorized=["name", "title", "abstract"]) + + map_name = data.pop("name") + path = tools.mk_mapfile_path(map_name) + + with webapp.mightConflict("Mapfile", mapfile=map_name): + maptools.create_mapfile(path, map_name, data) - # TODO: Create mapfile - raise NotImplemented() webapp.Created("%s/maps/%s%s" % (web.ctx.home, map_name, (".%s" % format) if format else "")) @@ -108,6 +113,30 @@ class named_mapfile(object): "content": data}) } if format != "map" else data + @HTTPCompatible() + def PUT(self, map_name, format): + mf = get_mapfile(map_name) + path = tools.mk_mapfile_path(map_name) + + data = get_data(name="mapfile", mandatory=["name"], authorized=["name", "title", "abstract"]) + if map_name != data.pop("name"): + raise webapp.Forbidden("Can't change the name of a mapfile.") + + with webapp.mightConflict("Mapfile", mapfile=map_name): + mf.update(data) + + mf.save() + + @HTTPCompatible() + def DELETE(self, map_name, format): + mf = get_mapfile(map_name) + path = tools.mk_mapfile_path(map_name) + + # TODO: We need to check if this mapfile is empty. + + with webapp.mightConflict("Mapfile", mapfile=map_name): + mf.delete() + class workspaces(object): @HTTPCompatible() diff --git a/src/tools.py b/src/tools.py index 8429aa69e7b8916c0d84dbf0a955cdf66600a5d7..1d3590d6de48c407de35087cd2eadd024f9c97e6 100644 --- a/src/tools.py +++ b/src/tools.py @@ -49,6 +49,7 @@ def get_config(key=None, mode='r'): exit('Error in configuration file: %s' % exc) return __config if key == None else __config[key] if key in __config else {} + def get_mapfile_paths(): """Generates a list of mapfile paths managed by Mapserver REST API.""" @@ -86,6 +87,9 @@ def safe_path_join(root, *args): raise webapp.Forbidden(message="path '%s' outside root directory." % (args)) return full_path +def get_mapfile_path(*args): + return safe_path_join(get_config('storage')['mapfiles'], *args) + def get_resource_path(*args): return safe_path_join(get_config('storage')['resources'], *args) @@ -118,14 +122,18 @@ def iter_styles(mapfile=None): if f not in used_styles: yield f - def mk_path(path): dirs = os.path.dirname(path) if not os.path.isdir(dirs): os.makedirs(dirs) +def mk_mapfile_path(name, *args): + path = get_mapfile_path(name, *args) + mk_path(path) + return path + def mk_ds_data_path(ws_name, name, *args): - path = get_dws_data_path(name, *args) + path = get_ds_data_path(name, *args) mk_path(path) return path