diff --git a/README.md b/README.md index 62e70b5..9bc5c24 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # netgis-client WebGIS-Client in development for NetGIS / Geoportal RLP. + +## Disclaimer: +In early development, things will change and break frequently! + +## Principles: +- **Small Footprint:** Because this map clients purpose is to be included in other websites in multiple instances, no frameworks are used to keep it lean. Obviously some libraries like OL are necessary (see dependencies), but the goal is to mostly rely on browser APIs only. +- **Backwards Compatibility:** This map client may be used in work environments with relatively old hardware and software. +- **Modularization:** Functionality is encapsulated in module classes (classic JS prototyped objects). +- **Programming Patterns:** At the core this client makes heavy use of the Observer and State patterns. Where possible pure functions shall be implemented to reduce side effects. + +## Dependencies: +### Core: +- OpenLayers 6 +- FontAwesome 5 + +### Optional: +- Proj4js +- JSTS +- ShapefileJS +- JSPDF +- GIFJS + +## Documentation: +Coming soon. See the "demo" folder for example implementations. \ No newline at end of file diff --git a/data/eden_context_mod.json b/data/eden_context_mod.json new file mode 100644 index 0000000..ad4650c --- /dev/null +++ b/data/eden_context_mod.json @@ -0,0 +1,101 @@ +{ + "type": "FeatureCollection", + "id": "http://localhost:8000/qm/gis/context?config=1", + "properties": { + "title": "Eden Map Configuration", + "updated": "2022-05-17T14:11:32Z", + "lang": "en" + }, + "features": [ + { + "type": "Feature", + "id": "https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi", + "properties": { + "title": "Population Density 2020", + "rights": "NASA Earthdata", + "updated": "2022-06-03T11:00:00Z", + "active": false, + "folder": "/test/population", + "offerings": [ + { + "code": "http://www.opengis.net/spec/owc-geojson/1.0/req/wms", + "operations": [ + { + "code": "GetMap", + "method": "GET", + "href": "https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi?layers=GPW_Population_Density_2020" + } + ] + } + ] + } + }, + { + "type": "Feature", + "id": "http://localhost:8000/qm/gis/layer_entity/59", + "properties": { + "title": "Cloud forecast ?", + "updated": "2022-04-27T09:02:14Z", + "active": false, + "folder": "/weather", + "offerings": [ + { + "code": "http://www.opengis.net/spec/owc-geojson/1.0/req/wms", + "operations": [ + { + "code": "GetCapabilities", + "method": "GET", + "href": "http://geo.weatheroffice.gc.ca/geomet?REQUEST=GetCapabilities&SERVICE=WMS&VERSION=1.1.1" + } + ] + } + ] + } + }, + { + "type": "Feature", + "id": "http://localhost:8000/qm/gis/layer_entity/23", + "properties": { + "title": "OpenStreetMap (Humanitarian)", + "rights": "OSM Contributors", + "updated": "2022-04-27T09:02:14Z", + "active": true, + "folder": "/base", + "offerings": [ + { + "code": "http://www.opengis.net/spec/owc-geojson/1.0/req/xyz", + "operations": [ + { + "code": "GetTile", + "method": "GET", + "href": "https://{a-c}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png" + } + ] + } + ] + } + }, + { + "type": "Feature", + "id": "http://localhost:8000/qm/gis/layer_entity/58", + "properties": { + "title": "Precipitation forecast ?", + "updated": "2022-04-27T09:02:14Z", + "active": false, + "folder": "/weather", + "offerings": [ + { + "code": "http://www.opengis.net/spec/owc-geojson/1.0/req/wms", + "operations": [ + { + "code": "GetCapabilities", + "method": "GET", + "href": "http://geo.weatheroffice.gc.ca/geomet?REQUEST=GetCapabilities&SERVICE=WMS&VERSION=1.1.1" + } + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/demo/basic.html b/demo/basic.html new file mode 100644 index 0000000..28299e9 --- /dev/null +++ b/demo/basic.html @@ -0,0 +1,84 @@ + + + + + + + + + + NetGIS Client + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/editor.html b/demo/editor.html new file mode 100644 index 0000000..289b8bf --- /dev/null +++ b/demo/editor.html @@ -0,0 +1,89 @@ + + + + + + + + + + NetGIS Client + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/ows.html b/demo/ows.html new file mode 100644 index 0000000..31d7a13 --- /dev/null +++ b/demo/ows.html @@ -0,0 +1,87 @@ + + + + + + + + + + NetGIS Client + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/netgis.min.css b/dist/netgis.min.css new file mode 100644 index 0000000..6aed1ec --- /dev/null +++ b/dist/netgis.min.css @@ -0,0 +1 @@ +.netgis-attribution{position:absolute;left:0;bottom:0;padding:1mm;background:rgba(255,255,255,0.5);font-size:.8em!important;z-index:100}.netgis-client{position:absolute;width:100%;height:100%;overflow:hidden;background:#aaa}.netgis-client *{box-sizing:border-box}.netgis-client .netgis-loader{position:absolute;width:100%;height:100%;z-index:9999;text-align:center;font-size:12mm}.netgis-client .netgis-loader.netgis-hide{display:none}.netgis-client .netgis-loader i{position:absolute;top:50%;transform:translateY(-50%);animation:netgis-spin 2s linear infinite}@keyframes netgis-spin{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}.netgis-controls{position:absolute;width:12mm;left:0;bottom:8mm;z-index:100}.netgis-controls button{font-size:6mm!important;width:100%;height:12mm;padding:0;border:none;background:none;cursor:pointer;color:white;text-shadow:-0.5mm 0 .3mm #000,.5mm 0 .3mm #000,0 -0.5mm .3mm #000,0 .5mm .3mm #000,0 1mm 3mm rgba(0,0,0,0.7)}.netgis-controls button:hover{text-shadow:-0.5mm 0 .3mm #a7233f,.5mm 0 .3mm #a7233f,0 -0.5mm .3mm #a7233f,0 .5mm .3mm #a7233f,0 1mm 3mm rgba(0,0,0,0.7)}.netgis-layer-list{position:absolute;right:0;width:100%;max-width:100mm;top:12mm;bottom:0;overflow:auto;z-index:200;-webkit-transform:none;transform:none;transition:transform 150ms ease}.netgis-layer-list.netgis-hide{-webkit-transform:translateX(110%);transform:translateX(110%);transition:transform 150ms ease;will-change:transform}.netgis-layer-list ul{list-style-type:none}.netgis-layer-list>ul{display:block;position:relative;width:100%;margin:0;padding:0}.netgis-folder{position:relative;overflow:hidden;list-style:none;padding:0;margin:0;min-height:12mm;width:100%;white-space:nowrap}.netgis-folder label{cursor:pointer}.netgis-folder input[type=checkbox]{cursor:pointer}.netgis-folder>button{display:inline-block;width:100%;padding:0;padding-right:16mm;margin:0;line-height:12mm;text-align:left}.netgis-folder>ul{display:none;padding-left:8mm}.netgis-folder.netgis-active>ul{display:block}.netgis-folder-item{height:12mm;line-height:12mm}.netgis-folder-item>label{display:block;padding-right:4mm}.netgis-layer-list .netgis-icon{display:inline-block;width:12mm;line-height:12mm;text-align:center}.netgis-layer-list i{margin-right:4mm}.netgis-folder i{color:#eab000}.netgis-folder-item i{color:#bbb}.netgis-folder .netgis-partial{opacity:.5}.netgis-map{position:absolute;left:0;right:0;top:12mm;bottom:0;background:#f2efe9}.netgis-drop-target{position:absolute;left:0;right:0;top:0;bottom:0;line-height:40mm;text-align:center;z-index:1;pointer-events:none;background:rgba(0,0,0,0.5);color:#fff}.netgis-drop-target.netgis-hide{display:none}.netgis-map{cursor:default}.netgis-map .netgis-toolbar,.netgis-map .netgis-dialog{cursor:auto}.netgis-map.netgis-mode-view,.netgis-map.netgis-mode-search-place{cursor:default;cursor:grab;cursor:-moz-grab;cursor:-webkit-grab}.netgis-map.netgis-mode-panning{cursor:move;cursor:all-scroll;cursor:grabbing;cursor:-moz-grabbing;cursor:-webkit-grabbing}.netgis-map.netgis-mode-zooming-in{cursor:zoom-in}.netgis-map.netgis-mode-zooming-out{cursor:zoom-out}.netgis-map.netgis-mode-draw-points,.netgis-map.netgis-mode-draw-lines,.netgis-map.netgis-mode-draw-polygons,.netgis-map.netgis-mode-cut-feature-draw,.netgis-map.netgis-mode-modify-features{cursor:pointer;cursor:crosshair}.netgis-map.netgis-mode-delete-features,.netgis-map.netgis-mode-cut-feature-begin,.netgis-map.netgis-mode-buffer-feature-begin{cursor:pointer}.netgis-map.netgis-mode-buffer-feature-edit{cursor:default}.netgis-menu{position:absolute;left:0;right:0;top:0;height:12mm;line-height:12mm;white-space:nowrap;font-size:0;z-index:1000;background:lightsalmon}.netgis-menu>div{height:12mm;white-space:nowrap}.netgis-menu>div>*{display:inline-block;height:100%;margin:0;font-size:4mm}.netgis-menu .netgis-right{float:right;padding-right:0;padding-left:4mm}.netgis-menu span{padding:0 4mm 0 0}.netgis-menu button i{font-size:5mm;width:12mm;line-height:12mm;text-align:center}.netgis-dropdown{position:relative;padding:0}.netgis-dropdown .netgis-dropdown-content{display:none;position:absolute;min-width:100%;padding:0;margin:0;margin-top:-1px;z-index:1;font-size:4mm;list-style-type:none}.netgis-dropdown:hover .netgis-dropdown-content{display:block}.netgis-dropdown .netgis-dropdown-content i{font-size:4mm;color:#bbb}.netgis-dropdown button{width:100%;padding:0 4mm 0 0;white-space:nowrap;text-align:left}.netgis-modal{display:none;position:absolute;width:100%;height:100%;top:0;left:0;padding:6mm;z-index:5000;background:rgba(0,0,0,0.6)}.netgis-modal.netgis-show{display:block}.netgis-modal .netgis-dialog{display:none;max-width:160mm;max-height:160mm;margin:auto;border-radius:1mm;border:1mm solid #a7233f;overflow:hidden;overflow-y:auto;cursor:default}.netgis-modal .netgis-dialog.netgis-show{display:block}.netgis-modal header{width:100%;height:12mm;line-height:12mm;text-align:center}.netgis-modal header button{position:relative;width:100%;min-height:12mm}.netgis-modal header button span{display:block;position:absolute;right:0;top:0;line-height:12mm;padding:0 4mm;text-align:center}.netgis-modal-content{padding:6mm;overflow:auto}.netgis-modal-content>table{width:100%;border:none;border-spacing:0}.netgis-modal-content>table td,.netgis-modal-content>table th{width:50%;overflow:hidden;vertical-align:top;text-align:left}.netgis-modal-content .netgis-padding{padding:4mm}.netgis-modal-content .netgis-space{height:6mm}.netgis-modal-content button,.netgis-modal-content label{width:100%;min-height:12mm}.netgis-modal-content button i{margin-right:4mm}.netgis-modal-content button[disabled]{opacity:.5;cursor:not-allowed}.netgis-modal-content input{width:100%}.netgis-modal-content input[type=file]{cursor:pointer}.netgis-modal-content ul{margin:0;padding:0;padding-left:4mm;list-style-type:square}.netgis-modal-content li:not(:last-child){margin-bottom:4mm}.netgis-modal h3{margin:0}.netgis-client,.netgis-client button{font-family:Verdana,sans-serif;font-size:4mm}.netgis-primary{background-color:#a7233f!important;color:white!important}.netgis-hover-primary:hover{background-color:#c82a4b!important;color:white!important}.netgis-hover-light:hover{background-color:#f4f4f4!important}.netgis-text-primary{color:#a7233f!important}.netgis-hover-text-primary:hover{color:#c82a4b!important}.netgis-dialog{background:#fff;color:#000}.netgis-dropdown:hover>button{background-color:#c82a4b!important}.netgis-shadow{box-shadow:0 .5mm 1mm 0 rgba(0,0,0,0.2),0 1mm 2.5mm 0 rgba(0,0,0,0.1)!important}.netgis-shadow-large{box-shadow:0 1mm 2mm 0 rgba(0,0,0,0.3),0 2mm 5mm 0 rgba(0,0,0,0.15)!important}.netgis-text-shadow{text-shadow:0 0 1mm rgba(0,0,0,1.0)}.netgis-client button{border:none;background:none;cursor:pointer}.netgis-clip-text{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.netgis-toolbars{position:absolute;left:0;right:0;top:0}.netgis-toolbar{position:absolute;left:0;right:0;top:12mm;min-height:12mm;line-height:12mm;font-size:0;white-space:nowrap;overflow-x:auto;overflow-y:hidden;z-index:1;-webkit-transform:none;transform:none;transition:transform 150ms ease}.netgis-toolbar.netgis-hide{-webkit-transform:translateY(-24mm);transform:translateY(-24mm);transition:transform 150ms ease;will-change:transform}.netgis-toolbar>div{height:12mm;white-space:nowrap}.netgis-toolbar>div>*{display:inline-block;font-size:4mm}.netgis-toolbar button{padding:0 4mm 0 0}.netgis-toolbar>div>button{line-height:12mm}.netgis-toolbar button i{width:12mm}.netgis-toolbar button:last-child{padding-right:0}.netgis-toolbar button:last-child span{margin-right:4mm}.netgis-toolbar label{display:inline-block;height:12mm;padding:0 4mm;cursor:pointer}.netgis-toolbar input[type=checkbox]{margin-right:2mm}.netgis-toolbar input[type=number]{margin-left:2mm;width:20mm}.netgis-toolbar input[type=text]{width:60mm}.netgis-toolbar .netgis-search-list{position:fixed;min-width:68mm;padding:0;margin:0;margin-left:-4mm;z-index:1;list-style-type:none;overflow:hidden;box-shadow:0 .5mm .5mm 0 rgba(0,0,0,0.2)!important}.netgis-toolbar .netgis-search-list.netgis-hide{display:none}.netgis-toolbar .netgis-search-list li{width:100%;padding:0;margin:0}.netgis-toolbar .netgis-search-list li button{width:100%;height:12mm;padding:0 4mm;white-space:nowrap;text-align:left} \ No newline at end of file diff --git a/dist/netgis.min.js b/dist/netgis.min.js new file mode 100644 index 0000000..7f8b60e --- /dev/null +++ b/dist/netgis.min.js @@ -0,0 +1,166 @@ +var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)}; +$jscomp.getGlobal=function(a){a=["object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;bc&&(c=Math.max(0,b+c));if(null==d||d>b)d=b;d=Number(d);0>d&&(d=Math.max(0,b+d));for(c=Number(c||0);cthis.callbacks[a].length&&delete this.callbacks[a]}else delete this.callbacks[a]};netgis.Client.prototype.invoke=function(a,b){this.debug&&console.info("EVENT:",a,b);if(netgis.util.isDefined(this.callbacks[a]))for(var c=0;c";a.title="Hineinzoomen";a.addEventListener("click",this.onZoomIn.bind(this));this.root.appendChild(a);a=document.createElement("button");a.setAttribute("type","button");a.innerHTML="";a.title="Herauszoomen";a.addEventListener("click", +this.onZoomOut.bind(this));this.root.appendChild(a);this.client.root.appendChild(this.root)};netgis.Controls.prototype.onZoomIn=function(a){this.client.invoke(netgis.Events.MAP_CHANGE_ZOOM,1)};netgis.Controls.prototype.onZoomOut=function(a){this.client.invoke(netgis.Events.MAP_CHANGE_ZOOM,-1)};netgis.Controls.prototype.onSettings=function(a){alert("TODO: settings dialog")};netgis=netgis||{}; +netgis.Events=Object.freeze({CONTEXT_UPDATE:"CONTEXT_UPDATE",SET_MODE:"SET_MODE",LAYER_LIST_TOGGLE:"LAYER_LIST_TOGGLE",PANEL_TOGGLE:"PANEL_TOGGLE",PANEL_SHOW:"PANEL_SHOW",PANEL_HIDE:"PANEL_HIDE",LAYER_SHOW:"LAYER_SHOW",LAYER_HIDE:"LAYER_HIDE",LAYER_CREATED:"LAYER_CREATED",MAP_SET_EXTENT:"MAP_SET_EXTENT",MAP_CHANGE_ZOOM:"MAP_CHANGE_ZOOM",MAP_UPDATE_STYLE:"MAP_UPDATE_STYLE",MAP_MODE_POINTS:"MAP_MODE_POINTS",MAP_MODE_LINES:"MAP_MODE_LINES",MAP_MODE_POLYGONS:"MAP_MODE_POLYGONS",EDIT_FEATURES_LOADED:"EDIT_FEATURES_LOADED",EDIT_FEATURES_CHANGE:"EDIT_FEATURES_CHANGE", +SEARCH_PLACE_REQUEST:"SEARCH_PLACE_REQUEST",SEARCH_PLACE_RESPONSE:"SEARCH_PLACE_RESPONSE",BUFFER_CHANGE:"BUFFER_CHANGE",BUFFER_ACCEPT:"BUFFER_ACCEPT",BUFFER_CANCEL:"BUFFER_CANCEL",SNAP_ON:"SNAP_ON",SNAP_OFF:"SNAP_OFF",IMPORT_SHAPEFILE_SHOW:"IMPORT_SHAPEFILE_SHOW",IMPORT_GEOJSON_SHOW:"IMPORT_GEOJSON_SHOW",IMPORT_GML_SHOW:"IMPORT_GML_SHOW",IMPORT_SHAPEFILE:"IMPORT_SHAPEFILE",IMPORT_GEOJSON:"IMPORT_GEOJSON",IMPORT_GML:"IMPORT_GML",EXPORT_PDF_SHOW:"EXPORT_PDF_SHOW",EXPORT_JPEG_SHOW:"EXPORT_JPEG_SHOW", +EXPORT_PNG_SHOW:"EXPORT_PNG_SHOW",EXPORT_GIF_SHOW:"EXPORT_GIF_SHOW",EXPORT_PDF:"EXPORT_PDF",EXPORT_JPEG:"EXPORT_JPEG",EXPORT_PNG:"EXPORT_PNG",EXPORT_GIF:"EXPORT_GIF",EXPORT_BEGIN:"EXPORT_BEGIN",EXPORT_END:"EXPORT_END"});netgis=netgis||{};netgis.LayerTree=function(){this.folderDraw=this.folderImport=this.list=this.root=this.client=null}; +netgis.LayerTree.prototype.load=function(){this.root=document.createElement("section");this.root.className="netgis-layer-list netgis-dialog netgis-shadow netgis-hide";this.list=document.createElement("ul");this.list.className="root";this.root.appendChild(this.list);this.client.root.appendChild(this.root);this.client.on(netgis.Events.CONTEXT_UPDATE,this.onContextUpdate.bind(this));this.client.on(netgis.Events.LAYER_LIST_TOGGLE,this.onLayerListToggle.bind(this));this.client.on(netgis.Events.LAYER_CREATED, +this.onLayerCreated.bind(this));this.client.on(netgis.Events.EDIT_FEATURES_CHANGE,this.onEditFeaturesChange.bind(this))};netgis.LayerTree.prototype.clearAll=function(){this.list.innerHTML=""}; +netgis.LayerTree.prototype.createFolder=function(a){var b=document.createElement("li");b.className="netgis-folder netgis-hover-light";b.setAttribute("title",a);var c=document.createElement("label");c.className="netgis-icon";b.appendChild(c);var d=document.createElement("input");d.setAttribute("type","checkbox");d.addEventListener("change",this.onFolderChange.bind(this));c.appendChild(d);c=document.createElement("button");c.setAttribute("type","button");c.className="netgis-clip-text netgis-hover-text-primary"; +c.innerHTML=''+a;c.addEventListener("click",this.onFolderClick.bind(this));b.appendChild(c);a=document.createElement("ul");b.appendChild(a);return b}; +netgis.LayerTree.prototype.createLayer=function(a,b,c){var d=document.createElement("li");d.setAttribute("title",b);d.className="netgis-folder-item netgis-hover-text-primary";var e=document.createElement("label");e.className="netgis-label netgis-clip-text";d.appendChild(e);var f=document.createElement("span");f.className="netgis-icon";e.appendChild(f);var g=document.createElement("input");g.setAttribute("type","checkbox");g.dataset.id=a;g.checked=c;g.addEventListener("change",this.onItemChange.bind(this)); +f.appendChild(g);a=document.createElement("i");a.className="fas fa-th-large";e.appendChild(a);b=document.createTextNode(b);e.appendChild(b);return d};netgis.LayerTree.prototype.addToFolder=function(a,b,c){a?(a=a.getElementsByTagName("ul")[0],a.appendChild(b)):a=this.list;c?a.insertBefore(b,a.firstChild):a.appendChild(b)};netgis.LayerTree.prototype.onFolderClick=function(a){a.currentTarget.parentElement.classList.toggle("netgis-active")}; +netgis.LayerTree.prototype.onFolderChange=function(a){var b=a.currentTarget;a=b.checked;b=b.parentElement.parentElement;for(var c=b.getElementsByTagName("input"),d=1;dn.width){var v=q;r=v*u;r>p&&(r=p,v=r/u)}else r=p,v=r/u,v>q&&(v=q,r=v*u);u=new jsPDF(d?"l":"p");var x=e;x+=(p-r)/2;p=e;p+=(q-v)/2;u.addImage(n.toDataURL("image/png,1.0",1),"PNG",x,p,r,v);u.setFontSize(8);u.text("Quelle: "+window.location.href,x+2,p+v-2);n=u.output("bloburl",{filename:h.export.defaultFilename+".pdf"});window.open(n,"_blank");break;case "jpeg":window.navigator.msSaveBlob? +window.navigator.msSaveBlob(n.msToBlob(),h.export.defaultFilename+".jpg"):(t.setAttribute("download",h.export.defaultFilename+".jpg"),t.setAttribute("href",n.toDataURL("image/jpeg",1)),t.click());break;case "png":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),h.export.defaultFilename+".png"):(t.setAttribute("download",h.export.defaultFilename+".png"),t.setAttribute("href",n.toDataURL("image/png",1)),t.click());break;case "gif":t.setAttribute("download",h.export.defaultFilename+ +".gif"),q=new GIF({workerScript:h.export.gifWebWorker,quality:1}),q.addFrame(n),q.on("finished",function(a){t.setAttribute("href",window.URL.createObjectURL(a));t.click()}),q.render()}k.setTarget(g);g.removeChild(m);f.client.invoke(netgis.Events.EXPORT_END,null)});k.renderSync()};l.src=h.export.logo}; +netgis.MapOpenLayers.prototype.splitMultiPolygons=function(a){a=a.getSource();for(var b=a.getFeatures(),c=[],d=[],e=0;e',!0);b.addEventListener("click",this.onToggleClick.bind(this));a.appendChild(b);b=this.createButton('Suche',!0);b.addEventListener("click",this.onSearchPlaceClick.bind(this)); +a.appendChild(b);if(this.client.editable){b=this.createMenu('Zeichnen');var c=b.getElementsByTagName("ul")[0];a.appendChild(b);c.appendChild(this.createMenuItem('Punkte',this.onDrawPointClick.bind(this)));c.appendChild(this.createMenuItem('Linien',this.onDrawLineClick.bind(this)));c.appendChild(this.createMenuItem('Polygone',this.onDrawPolygonClick.bind(this)));b=this.createMenu('Bearbeiten'); +c=b.getElementsByTagName("ul")[0];a.appendChild(b);c.appendChild(this.createMenuItem('Ausschneiden',this.onCutFeatureClick.bind(this)));c.appendChild(this.createMenuItem('Verschieben',this.onModifyFeaturesClick.bind(this)));c.appendChild(this.createMenuItem('L\u00f6schen',this.onDeleteFeaturesClick.bind(this)));c.appendChild(this.createMenuItem('Puffern',this.onBufferFeatureClick.bind(this))); +b=this.createMenu('Import');a.appendChild(b);b=b.getElementsByTagName("ul")[0];b.appendChild(this.createMenuItem('GeoJSON',this.onImportGeoJSONClick.bind(this)));b.appendChild(this.createMenuItem('GML',this.onImportGMLClick.bind(this)));b.appendChild(this.createMenuItem('Shapefile',this.onImportShapefileClick.bind(this)));b=this.createMenu('Export');a.appendChild(b); +a=b.getElementsByTagName("ul")[0];a.appendChild(this.createMenuItem('PDF',this.onExportPDFClick.bind(this)));a.appendChild(this.createMenuItem('JPEG',this.onExportJPEGClick.bind(this)));a.appendChild(this.createMenuItem('PNG',this.onExportPNGClick.bind(this)));a.appendChild(this.createMenuItem('GIF',this.onExportGIFClick.bind(this)))}this.client.root.appendChild(this.root)}; +netgis.Menu.prototype.createButton=function(a,b){var c=document.createElement("button");c.setAttribute("type","button");c.className="netgis-primary netgis-hover-primary";b&&(c.className+=" netgis-right");c.innerHTML=a;return c}; +netgis.Menu.prototype.createMenu=function(a){var b=document.createElement("div");b.className="netgis-dropdown";var c=document.createElement("button");c.setAttribute("type","button");c.className="netgis-primary netgis-hover-primary";c.innerHTML=a;b.appendChild(c);a=document.createElement("ul");a.className="netgis-dropdown-content netgis-dialog netgis-shadow";b.appendChild(a);return b}; +netgis.Menu.prototype.createMenuItem=function(a,b){var c=document.createElement("li");c.className="netgis-hover-light";var d=document.createElement("button");d.setAttribute("type","button");d.innerHTML=a;d.addEventListener("click",b);c.appendChild(d);return c};netgis.Menu.prototype.onToggleClick=function(a){this.client.invoke(netgis.Events.LAYER_LIST_TOGGLE,null)};netgis.Menu.prototype.onDrawPointClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.DRAW_POINTS)}; +netgis.Menu.prototype.onDrawLineClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.DRAW_LINES)};netgis.Menu.prototype.onDrawPolygonClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.DRAW_POLYGONS)};netgis.Menu.prototype.onCutFeatureClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.CUT_FEATURE_BEGIN)};netgis.Menu.prototype.onModifyFeaturesClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.MODIFY_FEATURES)}; +netgis.Menu.prototype.onDeleteFeaturesClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.DELETE_FEATURES)};netgis.Menu.prototype.onBufferFeatureClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.BUFFER_FEATURE_BEGIN)};netgis.Menu.prototype.onSearchPlaceClick=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.SEARCH_PLACE)};netgis.Menu.prototype.onSearchDataClick=function(a){alert("TODO: data search interface")}; +netgis.Menu.prototype.onImportOWSClick=function(a){alert("TODO: ows import interface, try setting url parameter '?ows='")};netgis.Menu.prototype.onImportShapefileClick=function(a){this.client.invoke(netgis.Events.IMPORT_SHAPEFILE_SHOW,null)};netgis.Menu.prototype.onImportGeoJSONClick=function(a){this.client.invoke(netgis.Events.IMPORT_GEOJSON_SHOW,null)};netgis.Menu.prototype.onImportKMLClick=function(a){alert("TODO: kml import interface")}; +netgis.Menu.prototype.onImportGMLClick=function(a){this.client.invoke(netgis.Events.IMPORT_GML_SHOW,null)};netgis.Menu.prototype.onExportPDFClick=function(a){this.client.invoke(netgis.Events.EXPORT_PDF_SHOW,null)};netgis.Menu.prototype.onExportJPEGClick=function(a){this.client.invoke(netgis.Events.EXPORT_JPEG_SHOW,null)};netgis.Menu.prototype.onExportPNGClick=function(a){this.client.invoke(netgis.Events.EXPORT_PNG_SHOW,null)}; +netgis.Menu.prototype.onExportGIFClick=function(a){this.client.invoke(netgis.Events.EXPORT_GIF_SHOW,null)};netgis=netgis||{};netgis.Modal=function(){this.exportGIF=this.exportPNG=this.exportJPEG=this.exportPDF=this.importShapefile=this.importGML=this.importGeoJSON=this.client=null}; +netgis.Modal.prototype.load=function(){this.root=document.createElement("section");this.root.className="netgis-modal";this.root.addEventListener("click",this.onRootClick.bind(this));this.importGeoJSON=this.createImportGeoJSON();this.root.appendChild(this.importGeoJSON);this.importGML=this.createImportGML();this.root.appendChild(this.importGML);this.importShapefile=this.createImportShapefile();this.root.appendChild(this.importShapefile);this.exportPDF=this.createExportPDF();this.root.appendChild(this.exportPDF); +this.exportJPEG=this.createExportJPEG();this.root.appendChild(this.exportJPEG);this.exportPNG=this.createExportPNG();this.root.appendChild(this.exportPNG);this.exportGIF=this.createExportGIF();this.root.appendChild(this.exportGIF);this.client.root.appendChild(this.root);this.client.on(netgis.Events.IMPORT_GEOJSON_SHOW,this.onImportGeoJSONShow.bind(this));this.client.on(netgis.Events.IMPORT_GML_SHOW,this.onImportGMLShow.bind(this));this.client.on(netgis.Events.IMPORT_SHAPEFILE_SHOW,this.onImportShapefileShow.bind(this)); +this.client.on(netgis.Events.EXPORT_PDF_SHOW,this.onExportPDFShow.bind(this));this.client.on(netgis.Events.EXPORT_JPEG_SHOW,this.onExportJPEGShow.bind(this));this.client.on(netgis.Events.EXPORT_PNG_SHOW,this.onExportPNGShow.bind(this));this.client.on(netgis.Events.EXPORT_GIF_SHOW,this.onExportGIFShow.bind(this))}; +netgis.Modal.prototype.createImportGeoJSON=function(){var a=this.createContainer("Import GeoJSON");this.createText(a,"Unterst\u00fctzte Koordinatensysteme:","
  • World Geodetic System 1984 (EPSG:4326)
  • ETRS89 / UTM zone 32N (EPSG:25832)
");this.createInputFile(a,"Datei ausw\u00e4hlen / ablegen:",".geojson,.json",this.onImportGeoJSONChange.bind(this));this.createSpace(a);this.createButton(a,"Importieren",this.onImportGeoJSONAccept.bind(this));return a}; +netgis.Modal.prototype.createImportGML=function(){var a=this.createContainer("Import GML");this.createText(a,"Unterst\u00fctzte Koordinatensysteme:","
  • World Geodetic System 1984 (EPSG:4326)
  • ETRS89 / UTM zone 32N (EPSG:25832)
");this.createInputFile(a,"Datei ausw\u00e4hlen / ablegen:",".gml,.xml",this.onImportGMLChange.bind(this));this.createSpace(a);this.createButton(a,"Importieren",this.onImportGMLAccept.bind(this));return a}; +netgis.Modal.prototype.createImportShapefile=function(){var a=this.createContainer("Import Shapefile");this.createText(a,"Unterst\u00fctzte Koordinatensysteme:","
  • World Geodetic System 1984 (EPSG:4326)
  • ETRS89 / UTM zone 32N (EPSG:25832)
");this.createInputFile(a,"Datei ausw\u00e4hlen / ablegen:","application/zip",this.onImportShapefileChange.bind(this));this.createSpace(a);this.createButton(a,"Importieren",this.onImportShapefileAccept.bind(this)); +return a};netgis.Modal.prototype.createExportPDF=function(){var a=this.createContainer("Export PDF");this.createInputInteger(a,"Breite (Pixel):",800,0,4096);this.createInputInteger(a,"H\u00f6he (Pixel):",600,0,4096);this.createInputInteger(a,"Seitenr\u00e4nder (Millimeter):",10,0,100);this.createInputCheckbox(a,"Querformat:",!0);this.createSpace(a);this.createButton(a,"Exportieren",this.onExportPDFAccept.bind(this));return a}; +netgis.Modal.prototype.createExportJPEG=function(){var a=this.createContainer("Export JPEG");this.createInputInteger(a,"Breite (Pixel):",800,0,4096);this.createInputInteger(a,"H\u00f6he (Pixel):",600,0,4096);this.createSpace(a);this.createButton(a,"Exportieren",this.onExportJPEGAccept.bind(this));return a}; +netgis.Modal.prototype.createExportPNG=function(){var a=this.createContainer("Export PNG");this.createInputInteger(a,"Breite (Pixel):",800,0,4096);this.createInputInteger(a,"H\u00f6he (Pixel):",600,0,4096);this.createSpace(a);this.createButton(a,"Exportieren",this.onExportPNGAccept.bind(this));return a}; +netgis.Modal.prototype.createExportGIF=function(){var a=this.createContainer("Export GIF");this.createInputInteger(a,"Breite (Pixel):",800,0,4096);this.createInputInteger(a,"H\u00f6he (Pixel):",600,0,4096);this.createSpace(a);this.createButton(a,"Exportieren",this.onExportGIFAccept.bind(this));return a}; +netgis.Modal.prototype.show=function(a){this.root.classList.add("netgis-show");for(var b=this.root.getElementsByClassName("netgis-dialog"),c=0;c";d.addEventListener("click",this.onHeaderClick.bind(this));c.appendChild(d);a=document.createElement("span");a.innerHTML="";d.appendChild(a);b.appendChild(c); +c=document.createElement("div");c.className="netgis-modal-content";b.appendChild(c);d=document.createElement("table");c.appendChild(d);return b};netgis.Modal.prototype.createSpace=function(a){var b=document.createElement("tr");b.className="netgis-space";var c=document.createElement("td");c.setAttribute("colspan",100);b.appendChild(c);a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(b);return c}; +netgis.Modal.prototype.createButton=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("td");e.setAttribute("colspan",100);d.appendChild(e);var f=document.createElement("button");f.setAttribute("type","button");f.className="netgis-primary netgis-hover-primary";f.innerHTML=b;c&&f.addEventListener("click",c);e.appendChild(f);a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(d);return f}; +netgis.Modal.prototype.createInputText=function(a,b){var c=document.createElement("tr"),d=document.createElement("th");d.className="netgis-padding";var e=document.createElement("label");e.innerHTML=b;d.appendChild(e);c.appendChild(d);b=document.createElement("td");b.className="netgis-padding";d=document.createElement("input");d.setAttribute("type","text");b.appendChild(d);c.appendChild(b);e.htmlFor=d;a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(c); +return d}; +netgis.Modal.prototype.createInputInteger=function(a,b,c,d,e){var f=document.createElement("tr"),g=document.createElement("th");g.className="netgis-padding";var k=document.createElement("label");k.innerHTML=b;g.appendChild(k);f.appendChild(g);b=document.createElement("td");b.className="netgis-padding";g=document.createElement("input");g.setAttribute("type","number");g.setAttribute("min",d);g.setAttribute("max",e);g.value=Number.parseInt(c);b.appendChild(g);f.appendChild(b);k.htmlFor=g;a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(f);return g}; +netgis.Modal.prototype.createInputCheckbox=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("th");e.className="netgis-padding";var f=document.createElement("label");f.innerHTML=b;e.appendChild(f);d.appendChild(e);b=document.createElement("td");b.className="netgis-padding";e=document.createElement("input");e.setAttribute("type","checkbox");e.checked=c;b.appendChild(e);d.appendChild(b);f.htmlFor=e;a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(d); +return e}; +netgis.Modal.prototype.createInputFile=function(a,b,c,d){var e=document.createElement("tr"),f=document.createElement("th");f.className="netgis-padding";var g=document.createElement("label");g.innerHTML=b;f.appendChild(g);e.appendChild(f);b=document.createElement("td");b.className="netgis-padding";f=document.createElement("input");f.setAttribute("type","file");f.setAttribute("accept",c);d&&f.addEventListener("change",d);b.appendChild(f);e.appendChild(b);a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(e);return f}; +netgis.Modal.prototype.createText=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("th");e.className="netgis-padding";e.innerHTML=b;d.appendChild(e);b=document.createElement("td");b.className="netgis-padding";b.innerHTML=c;d.appendChild(b);a.getElementsByClassName("netgis-modal-content")[0].getElementsByTagName("table")[0].appendChild(d);return b};netgis.Modal.prototype.onRootClick=function(a){a.target===this.root&&this.hide()};netgis.Modal.prototype.onHeaderClick=function(a){this.hide()}; +netgis.Modal.prototype.onImportGeoJSONShow=function(a){a=this.importGeoJSON.getElementsByTagName("input")[0];var b=this.importGeoJSON.getElementsByTagName("button")[1];a.value="";b.disabled=!0;this.show(this.importGeoJSON)};netgis.Modal.prototype.onImportGeoJSONChange=function(a){a=this.importGeoJSON.getElementsByTagName("input")[0];this.importGeoJSON.getElementsByTagName("button")[1].disabled=a.value&&0Punkte zeichnen:',this.onToolbarClose.bind(this)));this.append(this.toolbars[netgis.Modes.DRAW_POINTS],this.createToolbarCheckbox("Einrasten",this.onSnapChange.bind(this))); +this.root.appendChild(this.toolbars[netgis.Modes.DRAW_POINTS]);this.toolbars[netgis.Modes.DRAW_LINES]=this.createToolbar();this.append(this.toolbars[netgis.Modes.DRAW_LINES],this.createToolbarButton('Linien zeichnen:',this.onToolbarClose.bind(this)));this.append(this.toolbars[netgis.Modes.DRAW_LINES],this.createToolbarCheckbox("Einrasten",this.onSnapChange.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.DRAW_LINES]);this.toolbars[netgis.Modes.DRAW_POLYGONS]= +this.createToolbar();this.append(this.toolbars[netgis.Modes.DRAW_POLYGONS],this.createToolbarButton('Polygone zeichnen:',this.onToolbarClose.bind(this)));this.append(this.toolbars[netgis.Modes.DRAW_POLYGONS],this.createToolbarCheckbox("Einrasten",this.onSnapChange.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.DRAW_POLYGONS]);this.toolbars[netgis.Modes.CUT_FEATURE_BEGIN]=this.createToolbar();this.append(this.toolbars[netgis.Modes.CUT_FEATURE_BEGIN], +this.createToolbarButton('Feature zum Ausschneiden w\u00e4hlen:',this.onToolbarClose.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.CUT_FEATURE_BEGIN]);this.toolbars[netgis.Modes.CUT_FEATURE_DRAW]=this.createToolbar();this.append(this.toolbars[netgis.Modes.CUT_FEATURE_DRAW],this.createToolbarButton('Fl\u00e4che zum Ausschneiden zeichnen:',this.onToolbarClose.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.CUT_FEATURE_DRAW]); +this.toolbars[netgis.Modes.MODIFY_FEATURES]=this.createToolbar();this.append(this.toolbars[netgis.Modes.MODIFY_FEATURES],this.createToolbarButton('Features verschieben:',this.onToolbarClose.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.MODIFY_FEATURES]);this.toolbars[netgis.Modes.DELETE_FEATURES]=this.createToolbar();this.append(this.toolbars[netgis.Modes.DELETE_FEATURES],this.createToolbarButton('Features l\u00f6schen:', +this.onToolbarClose.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.DELETE_FEATURES]);this.toolbars[netgis.Modes.BUFFER_FEATURE_BEGIN]=this.createToolbar();this.append(this.toolbars[netgis.Modes.BUFFER_FEATURE_BEGIN],this.createToolbarButton('Feature zum Puffern w\u00e4hlen:',this.onToolbarClose.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.BUFFER_FEATURE_BEGIN]);this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT]=this.createToolbar(); +var b=1E3,c=3;netgis.util.isDefined(a.tools)&&(netgis.util.isDefined(a.tools.buffer.defaultRadius)&&(b=a.tools.buffer.defaultRadius),netgis.util.isDefined(a.tools.buffer.defaultSegments)&&(c=a.tools.buffer.defaultSegments));this.append(this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT],this.createToolbarButton('Feature puffern:',this.onBufferCancel.bind(this)));this.append(this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT],this.createToolbarInput("Radius in Meter:", +b,this.onBufferChange.bind(this)));this.append(this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT],this.createToolbarInput("Segmente:",c,this.onBufferChange.bind(this)));this.append(this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT],this.createToolbarButton('OK',this.onBufferAccept.bind(this)));a=this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT].getElementsByTagName("input");a[0].addEventListener("keyup",this.onBufferKeyUp.bind(this));a[1].addEventListener("keyup", +this.onBufferKeyUp.bind(this));a[1].setAttribute("min",1);this.root.appendChild(this.toolbars[netgis.Modes.BUFFER_FEATURE_EDIT])}this.toolbars[netgis.Modes.SEARCH_PLACE]=this.createToolbar();this.append(this.toolbars[netgis.Modes.SEARCH_PLACE],this.createToolbarButton('Suche:',this.onToolbarClose.bind(this)));a=this.createToolbarInputText("Adresse...","",null);a.style.position="relative";this.searchInput=a.getElementsByTagName("input")[0];this.searchInput.addEventListener("keyup", +this.onSearchKeyUp.bind(this));this.searchInput.addEventListener("focus",this.onSearchFocus.bind(this));this.searchInput.addEventListener("blur",this.onSearchBlur.bind(this));this.append(this.toolbars[netgis.Modes.SEARCH_PLACE],a);this.searchList=document.createElement("ul");this.searchList.className="netgis-dropdown-content netgis-search-list netgis-dialog netgis-shadow netgis-hide";a.appendChild(this.searchList);this.append(this.toolbars[netgis.Modes.SEARCH_PLACE],this.createToolbarButton('', +this.onSearchClear.bind(this)));this.root.appendChild(this.toolbars[netgis.Modes.SEARCH_PLACE]);this.client.root.appendChild(this.root);this.client.on(netgis.Events.SET_MODE,this.onSetMode.bind(this));this.client.on(netgis.Events.SEARCH_PLACE_RESPONSE,this.onSearchPlaceResponse.bind(this))};netgis.Toolbar.prototype.createToolbar=function(){var a=document.createElement("div");a.className="netgis-toolbar netgis-dialog netgis-shadow netgis-hide";var b=document.createElement("div");a.appendChild(b);return a}; +netgis.Toolbar.prototype.append=function(a,b){a.getElementsByTagName("div")[0].appendChild(b)};netgis.Toolbar.prototype.createToolbarButton=function(a,b){var c=document.createElement("button");c.setAttribute("type","button");c.className="netgis-hover-light";c.innerHTML=a;c.addEventListener("click",b);return c}; +netgis.Toolbar.prototype.createToolbarCheckbox=function(a,b){var c=document.createElement("label");c.className="netgis-hover-light";var d=document.createElement("input");d.setAttribute("type","checkbox");d.addEventListener("change",b);c.appendChild(d);a=document.createTextNode(a);c.appendChild(a);return c}; +netgis.Toolbar.prototype.createToolbarInput=function(a,b,c){var d=document.createElement("label");d.className="netgis-hover-light";a=document.createTextNode(a);d.appendChild(a);a=document.createElement("input");a.setAttribute("type","number");a.setAttribute("min",0);a.value=b;a.addEventListener("change",c);d.appendChild(a);return d}; +netgis.Toolbar.prototype.createToolbarInputText=function(a,b,c){var d=document.createElement("label");d.className="netgis-hover-light";var e=document.createElement("input");e.setAttribute("type","text");e.setAttribute("placeholder",a);e.value=b;c&&e.addEventListener("change",c);d.appendChild(e);return d}; +netgis.Toolbar.prototype.onSetMode=function(a){var b=!this.toolbars[netgis.Modes.SEARCH_PLACE].classList.contains("netgis-hide");netgis.util.foreach(this.toolbars,function(b,d){b===a?d.classList.remove("netgis-hide"):d.classList.add("netgis-hide")});switch(a){case netgis.Modes.SEARCH_PLACE:b?this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.VIEW):this.searchInput.focus();break;case netgis.Modes.BUFFER_FEATURE_EDIT:this.updateBuffer()}}; +netgis.Toolbar.prototype.onToolbarClose=function(a){this.client.invoke(netgis.Events.SET_MODE,netgis.Modes.VIEW)};netgis.Toolbar.prototype.searchRequest=function(a){a=a.trim();a!==this.searchValue&&(this.searchValue=a,0")},create:function(a){var b=document.createElement("tbody");b.innerHTML= +a;return b.children[0]},size:function(a){a=(new TextEncoder).encode(JSON.stringify(a)).length;var b=a/1024;return{bytes:a,kilobytes:b,megabytes:b/1024}},request:function(a,c){var b=new XMLHttpRequest;b.onload=function(){c(this.responseText)};b.open("GET",a,!0);b.send()},padstr:function(a,c){for(a=a.toString();a.length -

Hello World!

\ No newline at end of file diff --git a/src/netgis/Attribution.css b/src/netgis/Attribution.css new file mode 100644 index 0000000..50ff42f --- /dev/null +++ b/src/netgis/Attribution.css @@ -0,0 +1,11 @@ +.netgis-attribution +{ + position: absolute; + left: 0mm; + bottom: 0mm; + padding: 1mm; + background: rgba( 255, 255, 255, 0.5 ); + /*color: #a7233f;*/ + font-size: 0.8em !important; + z-index: 100; +} \ No newline at end of file diff --git a/src/netgis/Attribution.js b/src/netgis/Attribution.js new file mode 100644 index 0000000..ec73545 --- /dev/null +++ b/src/netgis/Attribution.js @@ -0,0 +1,86 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.Attribution = function() +{ + this.client = null; + this.layers = null; + this.items = []; +}; + +netgis.Attribution.prototype.load = function() +{ + this.root = document.createElement( "section" ); + this.root.className = "netgis-attribution netgis-text-primary"; + + if ( netgis.util.isDefined( this.client.config.map ) ) + if ( netgis.util.isDefined( this.client.config.map.attribution ) ) + this.items.push( this.client.config.map.attribution ); + + this.update(); + + this.client.root.appendChild( this.root ); + + // Events + this.client.on( netgis.Events.CONTEXT_UPDATE, this.onContextUpdate.bind( this ) ); + this.client.on( netgis.Events.LAYER_SHOW, this.onLayerShow.bind( this ) ); + this.client.on( netgis.Events.LAYER_HIDE, this.onLayerHide.bind( this ) ); +}; + +netgis.Attribution.prototype.update = function() +{ + this.root.innerHTML = "© " + this.items.join( ", " ); +}; + +netgis.Attribution.prototype.onContextUpdate = function( e ) +{ + var context = e; + + // Layers + this.layers = []; + + for ( var l = 0; l < context.layers.length; l++ ) + { + var item = context.layers[ l ]; + + if ( item.attribution && item.attribution.length > 0 ) + { + this.layers[ l ] = item.attribution; + } + } +}; + +netgis.Attribution.prototype.onLayerShow = function( e ) +{ + var attribution = this.layers[ e.id ]; + + if ( ! attribution ) return; + + for ( var i = 0; i < this.items.length; i++ ) + { + if ( this.items[ i ] === attribution ) return; + } + + this.items.push( attribution ); + + this.update(); +}; + +netgis.Attribution.prototype.onLayerHide = function( e ) +{ + var attribution = this.layers[ e.id ]; + + if ( ! attribution ) return; + + for ( var i = 0; i < this.items.length; i++ ) + { + if ( this.items[ i ] === attribution ) + { + this.items.splice( i, 1 ); + break; + } + } + + this.update(); +}; \ No newline at end of file diff --git a/src/netgis/Client.css b/src/netgis/Client.css new file mode 100644 index 0000000..ce252e0 --- /dev/null +++ b/src/netgis/Client.css @@ -0,0 +1,47 @@ +.netgis-client +{ + position: absolute; + width: 100%; + height: 100%; + overflow: hidden; + background: #aaa; +} + +.netgis-client * +{ + box-sizing: border-box; +} + +.netgis-client .netgis-loader +{ + position: absolute; + width: 100%; + height: 100%; + z-index: 9999; + text-align: center; + font-size: 12mm; +} + +.netgis-client .netgis-loader.netgis-hide +{ + display: none; +} + +.netgis-client .netgis-loader i +{ + position: absolute; + top: 50%; + transform: translateY( -50% ); + animation: netgis-spin 2s linear infinite; +} + +@keyframes netgis-spin +{ + 0% { transform: rotate( 0deg ); } + 100% { transform: rotate( 360deg ); } +} + +.netgis-client button +{ + /*outline: none;*/ +} \ No newline at end of file diff --git a/src/netgis/Client.js b/src/netgis/Client.js new file mode 100644 index 0000000..7153831 --- /dev/null +++ b/src/netgis/Client.js @@ -0,0 +1,298 @@ +"use strict"; + +/** + * The netgis namespace. + * @namespace + */ +var netgis = netgis || {}; + +/** + * The main NetGIS Client class. + * @param {Element} container + * @param {JSON} config + * @returns {netgis.Client} + */ +netgis.Client = function( container, config ) +{ + this.build = "20220704"; + this.debug = false; + + if ( netgis.util.isString( container ) ) + container = document.getElementById( container ); + + this.container = container; + + this.editable = true; + this.root = null; + + this.modules = []; + this.callbacks = {}; + + this.config = this.createDefaultConfig(); + + this.create(); + + if ( netgis.util.isDefined( config ) ) + { + if ( netgis.util.isString( config ) ) + { + // Config From Url + var self = this; + + netgis.util.request + ( + config, + function( data ) + { + var json = JSON.parse( data ); + + netgis.util.merge( self.config, json ); + + self.createModules(); + self.load(); + self.invoke( netgis.Events.CONTEXT_UPDATE, self.config ); + + self.hideLoader(); + } + ); + } + else + { + // Config From Object + netgis.util.merge( this.config, config ); + + this.createModules(); + this.load(); + this.invoke( netgis.Events.CONTEXT_UPDATE, this.config ); + + this.hideLoader(); + } + } + else + { + // No Config Given + this.createModules(); + this.load(); + this.invoke( netgis.Events.CONTEXT_UPDATE, this.config ); + + this.hideLoader(); + } + + //TODO: config module to handle params, existance, defaults, etc. ? +}; + +netgis.Client.prototype.createDefaultConfig = function() +{ + var config = + { + map: + { + projection: "EPSG:3857", + center: [ 1113194.0, 6621293.0 ], + minZoom: 0, + maxZoom: 20, + zoom: 6, + attribution: "NetGIS" + }, + projections: + [ + ], + layers: + [ + { folder: 0, type: netgis.LayerTypes.OSM, title: "Open Street Map", attribution: "OSM Contributors", active: true } + ], + folders: + [ + { title: "Hintergrund", parent: -1 } + ], + styles: + { + editLayer: { fill: "rgba( 255, 0, 0, 0.5 )", stroke: "#ff0000", strokeWidth: 3, pointRadius: 6 }, + select: { fill: "rgba( 0, 127, 255, 0.5 )", stroke: "#007fff", strokeWidth: 3, pointRadius: 6 }, + sketch: { fill: "rgba( 0, 127, 0, 0.5 )", stroke: "#007f00", strokeWidth: 3, pointRadius: 6 }, + modify: { fill: "rgba( 0, 127, 0, 0.5 )", stroke: "#007f00", strokeWidth: 3, pointRadius: 6 } + } + }; + + //TODO: advanced config merge, so it's easier to extend layers, styles etc. without replacing the whole array + + return config; +}; + +/** + * Creates the core HTML elements for this client. + */ +netgis.Client.prototype.create = function() +{ + this.root = document.createElement( "section" ); + this.root.className = "netgis-client"; + + this.loader = document.createElement( "div" ); + this.loader.className = "netgis-loader netgis-dialog netgis-text-primary"; + this.loader.innerHTML = ""; + this.root.appendChild( this.loader ); + + this.container.appendChild( this.root ); +}; + +/** + * Create and add all modules to this client. + */ +netgis.Client.prototype.createModules = function() +{ + // Editable + this.editable = true; + + if ( this.container.hasAttribute( "contenteditable" ) ) + { + if ( this.container.getAttribute( "contenteditable" ) === "false" ) + { + this.editable = false; + } + } + + if ( this.container.hasAttribute( "data-editable" ) ) + { + this.editable = this.container.getAttribute( "data-editable" ) === "true" ? true : false; + } + + // Modules + this.add( this.map = new netgis.MapOpenLayers() ); //TODO: how to properly store module references ? + this.add( new netgis.Controls() ); + this.add( new netgis.Attribution() ); + this.add( new netgis.LayerTree() ); + this.add( new netgis.Toolbar() ); + this.add( new netgis.Menu() ); + this.add( new netgis.SearchPlace() ); + this.add( new netgis.Modal() ); +}; + +/** + * Finally load this client and its modules. + */ +netgis.Client.prototype.load = function() +{ + // Modules + for ( var m = 0; m < this.modules.length; m++ ) + { + this.modules[ m ].load(); + } + + // Output Element + if ( netgis.util.isDefined( this.config.output ) ) + { + if ( netgis.util.isDefined( this.config.output.id ) ) + { + this.output = document.getElementById( this.config.output.id ); + + if ( this.output.value && this.output.value.length > 0 ) + { + var json = JSON.parse( this.output.value ); + this.invoke( netgis.Events.EDIT_FEATURES_LOADED, json ); + } + } + + } + else + { + this.output = document.createElement( "input" ); + this.output.setAttribute( "type", "hidden" ); + this.output.className = "netgis-edit-output"; + this.root.appendChild( this.output ); + } + + // Default Interaction + this.invoke( netgis.Events.SET_MODE, netgis.Modes.VIEW ); + + // Events + this.on( netgis.Events.EXPORT_BEGIN, this.onMapExportBegin.bind( this ) ); + this.on( netgis.Events.EXPORT_END, this.onMapExportEnd.bind( this ) ); + this.on( netgis.Events.EDIT_FEATURES_CHANGE, this.onEditFeaturesChange.bind( this ) ); +}; + +netgis.Client.prototype.add = function( module ) +{ + module.client = this; + this.modules.push( module ); +}; + +netgis.Client.prototype.on = function( evt, callback ) +{ + if ( ! netgis.util.isDefined( this.callbacks[ evt ] ) ) + { + this.callbacks[ evt ] = []; + } + + this.callbacks[ evt ].push( callback ); +}; + +netgis.Client.prototype.off = function( evt, callback ) +{ + if ( netgis.util.isDefined( this.callbacks[ evt ] ) ) + { + if ( netgis.util.isDefined( callback ) ) + { + // Remove Specific Callback + for ( var i = 0; i < this.callbacks[ evt ].length; i++ ) + { + if ( this.callbacks[ evt ][ i ] === callback ) + { + this.callbacks[ evt ].splice( i, 1 ); + break; + } + } + + if ( this.callbacks[ evt ].length < 1 ) delete this.callbacks[ evt ]; + } + else + { + // Remove All Callbacks + delete this.callbacks[ evt ]; + } + } +}; + +netgis.Client.prototype.invoke = function( evt, params ) +{ + if ( this.debug ) console.info( "EVENT:", evt, params ); + + if ( netgis.util.isDefined( this.callbacks[ evt ] ) ) + { + for ( var i = 0; i < this.callbacks[ evt ].length; i++ ) + { + this.callbacks[ evt ][ i ]( params ); + } + } +}; + +netgis.Client.prototype.showLoader = function() +{ + this.loader.classList.remove( "netgis-hide" ); +}; + +netgis.Client.prototype.hideLoader = function() +{ + this.loader.classList.add( "netgis-hide" ); +}; + +netgis.Client.prototype.onHtmlResponse = function( data ) +{ + this.root = netgis.util.create( data ); + this.container.appendChild( this.root ); +}; + +netgis.Client.prototype.onEditFeaturesChange = function( e ) +{ + var geojson = JSON.stringify( e ); + this.output.value = geojson; +}; + +netgis.Client.prototype.onMapExportBegin = function( e ) +{ + this.showLoader(); +}; + +netgis.Client.prototype.onMapExportEnd = function( e ) +{ + this.hideLoader(); +}; \ No newline at end of file diff --git a/src/netgis/Controls.css b/src/netgis/Controls.css new file mode 100644 index 0000000..3dd3929 --- /dev/null +++ b/src/netgis/Controls.css @@ -0,0 +1,59 @@ +.netgis-controls +{ + position: absolute; + width: 12mm; + left: 0mm; + bottom: 8mm; + + z-index: 100; + + /*background: lightblue;*/ +} + +.netgis-controls button +{ + font-size: 6mm !important; + + width: 100%; + height: 12mm; + padding: 0mm; + border: none; + background: none; + + cursor: pointer; + + color: white; + /*text-shadow: 0mm 0mm 1mm #a7233f;*/ + /*color: #a7233f;*/ + /*text-shadow: 0mm 0mm 1mm white, 0mm 0mm 2mm white, 0mm 0mm 3mm white;*/ + /*text-shadow: 0mm 0mm 1mm rgba( 0, 0, 0, 0.7 ), 0mm 0mm 3mm rgba( 0, 0, 0, 0.7 );*/ + + text-shadow: -0.5mm 0.0mm 0.3mm #000, + 0.5mm 0.0mm 0.3mm #000, + 0.0mm -0.5mm 0.3mm #000, + 0.0mm 0.5mm 0.3mm #000, + /*-0.4mm -0.4mm 0.3mm rgba( 0, 0, 0, 0.7 ), + 0.4mm -0.4mm 0.3mm rgba( 0, 0, 0, 0.7 ), + -0.4mm 0.4mm 0.3mm rgba( 0, 0, 0, 0.7 ), + 0.4mm 0.4mm 0.3mm rgba( 0, 0, 0, 0.7 ),*/ + 0mm 1mm 3mm rgba( 0, 0, 0, 0.7 ); +} + +.netgis-controls button:hover +{ + /*color: #a7233f;*/ + + /*color: white;*/ + /*text-shadow: 0mm 0mm 1mm #a7233f, 0mm 0mm 2mm #a7233f, 0mm 0mm 3mm #a7233f;*/ + /*text-shadow: 0mm 0mm 1mm rgba( 167, 35, 63, 0.7 ), 0mm 0mm 3mm rgba( 167, 35, 63, 0.7 );*/ + + text-shadow: -0.5mm 0.0mm 0.3mm #a7233f, + 0.5mm 0.0mm 0.3mm #a7233f, + 0.0mm -0.5mm 0.3mm #a7233f, + 0.0mm 0.5mm 0.3mm #a7233f, + /*-0.4mm -0.4mm 0.3mm rgba( 167, 35, 63, 0.7 ), + 0.4mm -0.4mm 0.3mm rgba( 167, 35, 63, 0.7 ), + -0.4mm 0.4mm 0.3mm rgba( 167, 35, 63, 0.7 ), + 0.4mm 0.4mm 0.3mm rgba( 167, 35, 63, 0.7 ),*/ + 0mm 1mm 3mm rgba( 0, 0, 0, 0.7 ); +} \ No newline at end of file diff --git a/src/netgis/Controls.js b/src/netgis/Controls.js new file mode 100644 index 0000000..bcce1df --- /dev/null +++ b/src/netgis/Controls.js @@ -0,0 +1,59 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.Controls = function() +{ + this.client = null; +}; + +netgis.Controls.prototype.load = function() +{ + this.root = document.createElement( "section" ); // header? + this.root.className = "netgis-controls"; + + /*var controls = document.createElement( "div" ); + controls.className = "netgis-controls"; + this.root.appendChild( controls );*/ + + var zoomIn = document.createElement( "button" ); + zoomIn.setAttribute( "type", "button" ); + zoomIn.innerHTML = ""; + zoomIn.title = "Hineinzoomen"; + zoomIn.addEventListener( "click", this.onZoomIn.bind( this ) ); + this.root.appendChild( zoomIn ); + + var zoomOut = document.createElement( "button" ); + zoomOut.setAttribute( "type", "button" ); + zoomOut.innerHTML = ""; + zoomOut.title = "Herauszoomen"; + zoomOut.addEventListener( "click", this.onZoomOut.bind( this ) ); + this.root.appendChild( zoomOut ); + + /*var settings = document.createElement( "button" ); + settings.innerHTML = ""; + settings.title = "Einstellungen"; + settings.addEventListener( "click", this.onSettings.bind( this ) ); + this.root.appendChild( settings );*/ + + this.client.root.appendChild( this.root ); +}; + +netgis.Controls.prototype.onZoomIn = function( e ) +{ + //this.view.adjustZoom( 1.0 ); + ////this.view.animate( { zoom: this.view.getZoom() + 1.0, duration: 200 } ); + this.client.invoke( netgis.Events.MAP_CHANGE_ZOOM, 1.0 ); +}; + +netgis.Controls.prototype.onZoomOut = function( e ) +{ + //this.view.adjustZoom( -1.0 ); + ////this.view.animate( { zoom: this.view.getZoom() - 1.0, duration: 200 } ); + this.client.invoke( netgis.Events.MAP_CHANGE_ZOOM, -1.0 ); +}; + +netgis.Controls.prototype.onSettings = function( e ) +{ + alert( "TODO: settings dialog" ); +}; \ No newline at end of file diff --git a/src/netgis/Events.js b/src/netgis/Events.js new file mode 100644 index 0000000..c1d591c --- /dev/null +++ b/src/netgis/Events.js @@ -0,0 +1,63 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.Events = Object.freeze +( + { + CONTEXT_UPDATE: "CONTEXT_UPDATE", + SET_MODE: "SET_MODE", + + LAYER_LIST_TOGGLE: "LAYER_LIST_TOGGLE", + + PANEL_TOGGLE: "PANEL_TOGGLE", + PANEL_SHOW: "PANEL_SHOW", + PANEL_HIDE: "PANEL_HIDE", + + LAYER_SHOW: "LAYER_SHOW", + LAYER_HIDE: "LAYER_HIDE", + LAYER_CREATED: "LAYER_CREATED", + + MAP_SET_EXTENT: "MAP_SET_EXTENT", + MAP_CHANGE_ZOOM: "MAP_CHANGE_ZOOM", + + MAP_UPDATE_STYLE: "MAP_UPDATE_STYLE", //TODO: ? + + MAP_MODE_POINTS: "MAP_MODE_POINTS", + MAP_MODE_LINES: "MAP_MODE_LINES", + MAP_MODE_POLYGONS: "MAP_MODE_POLYGONS", + + EDIT_FEATURES_LOADED: "EDIT_FEATURES_LOADED", + EDIT_FEATURES_CHANGE: "EDIT_FEATURES_CHANGE", + + SEARCH_PLACE_REQUEST: "SEARCH_PLACE_REQUEST", + SEARCH_PLACE_RESPONSE: "SEARCH_PLACE_RESPONSE", + + BUFFER_CHANGE: "BUFFER_CHANGE", + BUFFER_ACCEPT: "BUFFER_ACCEPT", + BUFFER_CANCEL: "BUFFER_CANCEL", + + SNAP_ON: "SNAP_ON", + SNAP_OFF: "SNAP_OFF", + + IMPORT_SHAPEFILE_SHOW: "IMPORT_SHAPEFILE_SHOW", + IMPORT_GEOJSON_SHOW: "IMPORT_GEOJSON_SHOW", + IMPORT_GML_SHOW: "IMPORT_GML_SHOW", + + IMPORT_SHAPEFILE: "IMPORT_SHAPEFILE", + IMPORT_GEOJSON: "IMPORT_GEOJSON", + IMPORT_GML: "IMPORT_GML", + + EXPORT_PDF_SHOW: "EXPORT_PDF_SHOW", + EXPORT_JPEG_SHOW: "EXPORT_JPEG_SHOW", + EXPORT_PNG_SHOW: "EXPORT_PNG_SHOW", + EXPORT_GIF_SHOW: "EXPORT_GIF_SHOW", + + EXPORT_PDF: "EXPORT_PDF", + EXPORT_JPEG: "EXPORT_JPEG", + EXPORT_PNG: "EXPORT_PNG", + EXPORT_GIF: "EXPORT_GIF", + EXPORT_BEGIN: "EXPORT_BEGIN", + EXPORT_END: "EXPORT_END" + } +); diff --git a/src/netgis/LayerTree.css b/src/netgis/LayerTree.css new file mode 100644 index 0000000..8e0f53d --- /dev/null +++ b/src/netgis/LayerTree.css @@ -0,0 +1,154 @@ +.netgis-layer-list +{ + position: absolute; + right: 0mm; + width: 100%; + max-width: 100mm; + top: 12mm; + bottom: 0mm; + overflow: auto; + z-index: 200; + + -webkit-transform: none; + transform: none; + transition: transform 150ms ease; +} + +.netgis-layer-list.netgis-hide +{ + -webkit-transform: translateX( 110% ); + transform: translateX( 110% ); + transition: transform 150ms ease; + will-change: transform; +} + +.netgis-layer-list ul +{ + list-style-type: none; +} + +.netgis-layer-list > ul +{ + display: block; + position: relative; + width: 100%; + margin: 0mm; + padding: 0mm; +} + +.netgis-folder +{ + position: relative; + overflow: hidden; + list-style: none; + padding: 0mm; + margin: 0mm; + min-height: 12mm; + width: 100%; + white-space: nowrap; +} + +.netgis-folder label +{ + /*/display: inline-block; + position: absolute; + width: 12mm; + height: 12mm; + left: 0mm; + top: 0mm; + text-align: center; + line-height: 12mm;*/ + cursor: pointer; +} + +.netgis-folder input[type=checkbox] +{ + cursor: pointer; +} + +.netgis-folder > button +{ + display: inline-block; + /*display: block; + position: absolute;*/ + /* width: auto; TODO: ??? */ + + width: 100%; + padding: 0mm; + padding-right: 16mm; /* 4mm + 12mm ( padding + checkbox width ) */ + + /*width: 88mm; /* 100mm - 12mm */ + /*width: calc( 100% - 12mm );*/ + /*width: literal( "calc(100%-12mm)" );*/ + + margin: 0mm; + + /*left: 12mm; + right: 0mm; + top: 0mm; + height: 12mm;*/ + line-height: 12mm; + text-align: left; + /*padding: 0mm; + padding-right: 4mm;*/ +} + +.netgis-folder > ul +{ + display: none; + /*max-height: 0mm; + overflow: hidden; + transition: max-height ease 200ms;*/ + + /*padding-top: 12mm;*/ + padding-left: 8mm; +} + +.netgis-folder.netgis-active > ul +{ + display: block; + /*max-height: 60mm; + overflow-y: auto;*/ +} + +.netgis-folder-item +{ + height: 12mm; + line-height: 12mm; +} + +.netgis-folder-item > label +{ + display: block; + /*width: 100%;*/ + padding-right: 4mm; +} + +/* TODO: just .netgis-icon for folders too */ +/*.netgis-folder-item*/ .netgis-layer-list .netgis-icon +{ + display: inline-block; + width: 12mm; + line-height: 12mm; + text-align: center; +} + +.netgis-layer-list i +{ + margin-right: 4mm; +} + +.netgis-folder i +{ + color: #eab000; +} + +.netgis-folder-item i +{ + color: #bbb; +} + +.netgis-folder .netgis-partial +{ + opacity: 0.5; +} diff --git a/src/netgis/LayerTree.js b/src/netgis/LayerTree.js new file mode 100644 index 0000000..129d74f --- /dev/null +++ b/src/netgis/LayerTree.js @@ -0,0 +1,380 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.LayerTree = function() +{ + this.client = null; + this.root = null; + this.list = null; + this.folderImport = null; + this.folderDraw = null; +}; + +netgis.LayerTree.prototype.load = function() +{ + this.root = document.createElement( "section" ); + this.root.className = "netgis-layer-list netgis-dialog netgis-shadow netgis-hide"; + //this.root.className = "netgis-layer-list netgis-dialog netgis-shadow"; + + this.list = document.createElement( "ul" ); + this.list.className = "root"; + this.root.appendChild( this.list ); + + this.client.root.appendChild( this.root ); + + this.client.on( netgis.Events.CONTEXT_UPDATE, this.onContextUpdate.bind( this ) ); + this.client.on( netgis.Events.LAYER_LIST_TOGGLE, this.onLayerListToggle.bind( this ) ); + this.client.on( netgis.Events.LAYER_CREATED, this.onLayerCreated.bind( this ) ); + this.client.on( netgis.Events.EDIT_FEATURES_CHANGE, this.onEditFeaturesChange.bind( this ) ); +}; + +netgis.LayerTree.prototype.clearAll = function() +{ + this.list.innerHTML = ""; +}; + +netgis.LayerTree.prototype.createFolder = function( title ) +{ + var item = document.createElement( "li" ); + item.className = "netgis-folder netgis-hover-light"; + item.setAttribute( "title", title ); + //item.innerHTML = "" + title + ""; + + var label = document.createElement( "label" ); + label.className = "netgis-icon"; + item.appendChild( label ); + + var checkbox = document.createElement( "input" ); + checkbox.setAttribute( "type", "checkbox" ); + //checkbox.checked = checked; + checkbox.addEventListener( "change", this.onFolderChange.bind( this ) ); + label.appendChild( checkbox ); + + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.className = "netgis-clip-text netgis-hover-text-primary"; + button.innerHTML = '' + title; + button.addEventListener( "click", this.onFolderClick.bind( this ) ); + item.appendChild( button ); + + var folder = document.createElement( "ul" ); + item.appendChild( folder ); + + return item; +}; + +netgis.LayerTree.prototype.createLayer = function( id, title, checked ) +{ + var item = document.createElement( "li" ); + //item.dataset.id = id; + item.setAttribute( "title", title ); + item.className = "netgis-folder-item netgis-hover-text-primary"; + + var label = document.createElement( "label" ); + label.className = "netgis-label netgis-clip-text"; + item.appendChild( label ); + + var span = document.createElement( "span" ); + span.className = "netgis-icon"; + label.appendChild( span ); + + var checkbox = document.createElement( "input" ); + checkbox.setAttribute( "type", "checkbox" ); + checkbox.dataset.id = id; + checkbox.checked = checked; + checkbox.addEventListener( "change", this.onItemChange.bind( this ) ); + span.appendChild( checkbox ); + + var icon = document.createElement( "i" ); + //icon.className = "fas fa-file"; + //icon.className = "fas fa-sticky-note"; + //icon.className = "far fa-map"; + icon.className = "fas fa-th-large"; + label.appendChild( icon ); + + var text = document.createTextNode( title ); + label.appendChild( text ); + + return item; +}; + +netgis.LayerTree.prototype.addToFolder = function( folder, item, prepend ) +{ + var list; + + if ( folder ) + { + list = folder.getElementsByTagName( "ul" )[ 0 ]; //TODO: folder.children[ 0 ] ? + list.appendChild( item ); + } + else + { + list = this.list; + } + + if ( ! prepend ) + { + list.appendChild( item ); + } + else + { + list.insertBefore( item, list.firstChild ); + } +}; + +netgis.LayerTree.prototype.onFolderClick = function( e ) +{ + /* + var folder = e.currentTarget.parentElement.parentElement; + folder.classList.toggle( "netgis-active" ); + */ + + //e.currentTarget.parentElement.querySelector(".nested").classList.toggle("active"); + + var folder = e.currentTarget.parentElement; + folder.classList.toggle( "netgis-active" ); +}; + +/*netgis.LayerTree.prototype.updateFolderCheck = function( folder ) +{ + var items = folder.getElementsByTagName( "input" ); + + +};*/ + +netgis.LayerTree.prototype.onFolderChange = function( e ) +{ + var checkbox = e.currentTarget; + var checked = checkbox.checked; + var folder = checkbox.parentElement.parentElement; + var items = folder.getElementsByTagName( "input" ); + //var items = folder.getElementsByClassName( "netgis-folder-item" ); + + //console.info( "Folder Change:", checked, folder, items ); + + // Check Child Items + for ( var i = 1; i < items.length; i++ ) + { + var item = items[ i ]; + var childcheck = item; + //var childcheck = item.getElementsByTagName( "input" )[ 0 ]; + + childcheck.checked = checked; + + //console.info( "Folder Child:", item, childcheck, id, items ); + + //var id = parseInt( childcheck.dataset.id ); + var id = childcheck.dataset.id; + + if ( netgis.util.isDefined( id ) ) + { + id = parseInt( id ); + this.client.invoke( checked ? netgis.Events.LAYER_SHOW : netgis.Events.LAYER_HIDE, { id: id } ); + } + } + + this.updateFolderChecks( folder ); + + // Check Parent Folder + var parentFolder = folder.parentElement.parentElement; + if ( parentFolder.className.search( "netgis-folder" ) !== -1 ) + this.updateFolderChecks( parentFolder ); +}; + +netgis.LayerTree.prototype.updateFolderChecks = function( folder ) +{ + if ( ! netgis.util.isDefined( folder ) ) folder = this.list; + + // Count Child Checks + var items = folder.getElementsByClassName( "netgis-folder-item" ); + + var checks = 0; + + for ( var i = 0; i < items.length; i++ ) + { + var checkbox = items[ i ].getElementsByTagName( "input" )[ 0 ]; + if ( checkbox.checked ) checks++; + } + + // Set Checkbox State + var checkbox = folder.getElementsByTagName( "input" )[ 0 ]; + + var state = 0; + if ( checks > 0 ) state = 1; + if ( checks === items.length ) state = 2; + + switch ( state ) + { + case 0: + { + // Unchecked + checkbox.checked = false; + checkbox.classList.remove( "netgis-partial" ); + break; + } + + case 1: + { + // Partially Checked + checkbox.checked = true; + checkbox.classList.add( "netgis-partial" ); + break; + } + + case 2: + { + // Fully Checked + checkbox.checked = true; + checkbox.classList.remove( "netgis-partial" ); + break; + } + } + + //TODO: use nearest ancestor selector + var parentList = folder.parentElement; + + if ( parentList && parentList !== this.list ) + { + // Recursion + var parentFolder = parentList.parentElement; + if ( parentFolder && parentFolder.className.search( "netgis-folder" ) !== -1 ) + this.updateFolderChecks( parentFolder ); + } +}; + +netgis.LayerTree.prototype.onItemChange = function( e ) +{ + var checkbox = e.currentTarget; + var checked = checkbox.checked; + var listitem = checkbox.parentElement.parentElement.parentElement; + var id = parseInt( checkbox.dataset.id ); + var folder = listitem.parentElement.parentElement; + var items = folder.getElementsByTagName( "input" ); + + // Check Parent Folder + var checks = 0; + + for ( var i = 1; i < items.length; i++ ) + { + var item = items[ i ]; + if ( item.checked ) checks++; + } + + if ( folder.className.search( "netgis-folder" ) !== -1 ) + this.updateFolderChecks( folder ); + + this.client.invoke( checked ? netgis.Events.LAYER_SHOW : netgis.Events.LAYER_HIDE, { id: id } ); +}; + +netgis.LayerTree.prototype.onLayerListToggle = function( e ) +{ + this.root.classList.toggle( "netgis-hide" ); +}; + +netgis.LayerTree.prototype.onContextUpdate = function( e ) +{ + this.clearAll(); + + var folders = e.folders; + var layers = e.layers; + + // Create Folders + var folderItems = []; + + for ( var f = 0; f < folders.length; f++ ) + { + var folder = folders[ f ]; + + var item = this.createFolder( folder.title ); + folderItems.push( item ); + } + + // Create Layers + for ( var l = 0; l < layers.length; l++ ) + { + var layer = layers[ l ]; + + var item = this.createLayer( l, layer.title, layer.active ); + this.addToFolder( folderItems[ layer.folder ], item ); + } + + // Append Folders + for ( var f = 0; f < folders.length; f++ ) + { + var folder = folders[ f ]; + var item = folderItems[ f ]; + + if ( folder.parent === -1 ) + { + this.list.appendChild( item ); + } + else + { + this.addToFolder( folderItems[ folder.parent ], item ); + } + } + + // Active State + for ( var l = 0; l < layers.length; l++ ) + { + var layer = layers[ l ]; + if ( layer.active ) this.client.invoke( netgis.Events.LAYER_SHOW, { id: l } ); + } + for ( var f = 0; f < folderItems.length; f++ ) + { + this.updateFolderChecks( folderItems[ f ] ); + } +}; + +netgis.LayerTree.prototype.onLayerCreated = function( e ) +{ + var item = this.createLayer( e.id, e.title, e.checked ); + + var folder; + + //TODO: this is a hack to get special folders working + if ( e.folder === "import" ) + { + if ( ! this.folderImport ) + { + this.folderImport = this.createFolder( "Importierte Ebenen" ); + this.list.insertBefore( this.folderImport, this.folderDraw ? this.folderDraw.nextSibling : this.list.firstChild ); + } + + folder = this.folderImport; + } + else if ( e.folder === "draw" ) + { + if ( ! this.folderDraw ) + { + this.folderDraw = this.createFolder( "Zeichnung" ); + this.list.insertBefore( this.folderDraw, this.list.firstChild ); + } + + folder = this.folderDraw; + } + + this.addToFolder( folder, item, true ); + this.updateFolderChecks( folder ); +}; + +netgis.LayerTree.prototype.onEditFeaturesChange = function( e ) +{ + // Enable draw layer if not already + if ( this.folderDraw ) + { + var list = this.folderDraw.getElementsByTagName( "ul" )[ 0 ]; + var checks = list.getElementsByTagName( "input" ); + var checkbox = checks[ 0 ]; + var id = parseInt( checkbox.dataset.id ); + + if ( ! checkbox.checked ) + { + checkbox.checked = true; + + this.updateFolderChecks( this.folderDraw ); + this.client.invoke( netgis.Events.LAYER_SHOW, { id: id } ); + } + }; +}; \ No newline at end of file diff --git a/src/netgis/LayerTypes.js b/src/netgis/LayerTypes.js new file mode 100644 index 0000000..677a039 --- /dev/null +++ b/src/netgis/LayerTypes.js @@ -0,0 +1,14 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.LayerTypes = Object.freeze +( + { + XYZ: "XYZ", + OSM: "OSM", + WMS: "WMS", + WFS: "WFS", + KML: "KML" + } +); \ No newline at end of file diff --git a/src/netgis/Map.css b/src/netgis/Map.css new file mode 100644 index 0000000..c9717b0 --- /dev/null +++ b/src/netgis/Map.css @@ -0,0 +1,102 @@ +.netgis-map +{ + position: absolute; + left: 0mm; + right: 0mm; + top: 12mm; + bottom: 0mm; + + background: #f2efe9; +} + +/* Drag and Drop */ + +/* +.netgis-map.drop +{ + border: 10mm solid red; +} +*/ + +.netgis-drop-target +{ + position: absolute; + left: 0mm; + right: 0mm; + top: 0mm; + bottom: 0mm; + line-height: 40mm; + text-align: center; + z-index: 1; + pointer-events: none; + background: rgba( 0, 0, 0, 0.5 ); + color: #fff; +} + +.netgis-drop-target.netgis-hide +{ + display: none; +} + +/* Modes */ + +.netgis-map +{ + cursor: default; +} + +.netgis-map .netgis-toolbar, +.netgis-map .netgis-dialog +{ + cursor: auto; +} + +.netgis-map.netgis-mode-view, +.netgis-map.netgis-mode-search-place +{ + cursor: default; + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; +} + +.netgis-map.netgis-mode-panning +{ + cursor: move; + cursor: all-scroll; + cursor: grabbing; + cursor: -moz-grabbing; + cursor: -webkit-grabbing; +} + +.netgis-map.netgis-mode-zooming-in +{ + cursor: zoom-in; +} + +.netgis-map.netgis-mode-zooming-out +{ + cursor: zoom-out; +} + +.netgis-map.netgis-mode-draw-points, +.netgis-map.netgis-mode-draw-lines, +.netgis-map.netgis-mode-draw-polygons, +.netgis-map.netgis-mode-cut-feature-draw, +.netgis-map.netgis-mode-modify-features +{ + cursor: pointer; + cursor: crosshair; +} + +.netgis-map.netgis-mode-delete-features, +.netgis-map.netgis-mode-cut-feature-begin, +.netgis-map.netgis-mode-buffer-feature-begin +{ + cursor: pointer; +} + +.netgis-map.netgis-mode-buffer-feature-edit +{ + cursor: default; +} \ No newline at end of file diff --git a/src/netgis/Map.js b/src/netgis/Map.js new file mode 100644 index 0000000..c2a6ab8 --- /dev/null +++ b/src/netgis/Map.js @@ -0,0 +1,20 @@ +"use strict"; + +var netgis = netgis || {}; + +//TODO: this common Map class is probably deprecated, no need for inheritance ? + +netgis.Map = function() +{ + this.client = null; + this.root = null; + this.attribution = null; +}; + +netgis.Map.prototype.load = function() +{ + this.root = document.createElement( "section" ); + this.root.className = "netgis-map"; + + this.client.root.appendChild( this.root ); +}; \ No newline at end of file diff --git a/src/netgis/MapOpenLayers.js b/src/netgis/MapOpenLayers.js new file mode 100644 index 0000000..49234ca --- /dev/null +++ b/src/netgis/MapOpenLayers.js @@ -0,0 +1,1599 @@ +/* global ol, jsts, shp, proj4 */ + +"use strict"; + +var netgis = netgis || {}; + +/** + * Map module implementation for OpenLayers 6+. + */ +netgis.MapOpenLayers = function() +{ + netgis.Map.call( this ); + + this.mode = null; + this.toolbars = {}; + this.view = null; + this.map = null; + this.layers = []; + this.interactions = {}; + this.snap = null; + this.snapFeatures = null; + this.editLayer = null; + this.hover = null; + this.selected = null; + this.sketch = null; + + this.editEventsSilent = false; + + this.importLayerID = 1000; + this.editLayerID = 2000; +}; + +netgis.MapOpenLayers.prototype = Object.create( netgis.Map.prototype ); +netgis.MapOpenLayers.prototype.constructor = netgis.MapOpenLayers; + +//TODO: not much benefits from Map class inheritance, may be dropped soon + +netgis.MapOpenLayers.prototype.load = function() +{ + // Elements + netgis.Map.prototype.load.call( this ); //TODO: ? + + this.dropTarget = document.createElement( "div" ); + this.dropTarget.className = "netgis-drop-target netgis-hide"; + this.dropTarget.innerHTML = "Datei hier ablegen!"; + this.root.appendChild( this.dropTarget ); + + this.root.addEventListener( "dragenter", this.onDragEnter.bind( this ) ); + this.root.addEventListener( "dragover", this.onDragEnter.bind( this ) ); + this.root.addEventListener( "dragend", this.onDragLeave.bind( this ) ); + this.root.addEventListener( "dragleave", this.onDragLeave.bind( this ) ); + this.root.addEventListener( "drop", this.onDragDrop.bind( this ) ); + + // Map Renderer + this.createMap(); + this.createDefaultLayers(); + this.createInteractions(); + + // Events + this.client.on( netgis.Events.CONTEXT_UPDATE, this.onContextUpdate.bind( this ) ); + this.client.on( netgis.Events.MAP_UPDATE_STYLE, this.onUpdateStyle.bind( this ) ); + this.client.on( netgis.Events.EDIT_FEATURES_LOADED, this.onEditFeaturesLoaded.bind( this ) ); + this.client.on( netgis.Events.SET_MODE, this.onSetMode.bind( this ) ); + this.client.on( netgis.Events.SNAP_ON, this.onSnapOn.bind( this ) ); + this.client.on( netgis.Events.SNAP_OFF, this.onSnapOff.bind( this ) ); + this.client.on( netgis.Events.LAYER_SHOW, this.onLayerShow.bind( this ) ); + this.client.on( netgis.Events.LAYER_HIDE, this.onLayerHide.bind( this ) ); + this.client.on( netgis.Events.MAP_SET_EXTENT, this.onSetExtent.bind( this ) ); + this.client.on( netgis.Events.MAP_CHANGE_ZOOM, this.onChangeZoom.bind( this ) ); + this.client.on( netgis.Events.BUFFER_CHANGE, this.onBufferChange.bind( this ) ); + this.client.on( netgis.Events.BUFFER_ACCEPT, this.onBufferAccept.bind( this ) ); + this.client.on( netgis.Events.BUFFER_CANCEL, this.onBufferCancel.bind( this ) ); + this.client.on( netgis.Events.IMPORT_GEOJSON, this.onImportGeoJSON.bind( this ) ); + this.client.on( netgis.Events.IMPORT_GML, this.onImportGML.bind( this ) ); + this.client.on( netgis.Events.IMPORT_SHAPEFILE, this.onImportShapefile.bind( this ) ); + this.client.on( netgis.Events.EXPORT_PDF, this.onExportPDF.bind( this ) ); + this.client.on( netgis.Events.EXPORT_JPEG, this.onExportJPEG.bind( this ) ); + this.client.on( netgis.Events.EXPORT_PNG, this.onExportPNG.bind( this ) ); + this.client.on( netgis.Events.EXPORT_GIF, this.onExportGIF.bind( this ) ); +}; + +netgis.MapOpenLayers.prototype.createMap = function() +{ + var config = this.client.config; + + // Projections ( WGS / Lon-Lat supported out of the box ) + if ( typeof proj4 !== "undefined" ) + { + proj4.defs( config.projections ); + proj4.defs( "urn:ogc:def:crs:OGC:1.3:CRS84", proj4.defs( "EPSG:4326" ) ); + ol.proj.proj4.register( proj4 ); + } + + // View + var viewParams = + { + projection: config.map.projection, + center: config.map.center, + minZoom: config.map.minZoom, + maxZoom: config.map.maxZoom, + zoom: config.map.zoom + }; + + this.view = new ol.View + ( + viewParams + ); + + // Map + this.map = new ol.Map + ( + { + target: this.root, + view: this.view, + pixelRatio: 1.0, + moveTolerance: 5, + controls: [] + } + ); + + this.map.on( "pointermove", this.onPointerMove.bind( this ) ); + this.map.on( "click", this.onSingleClick.bind( this ) ); + + this.map.on( "movestart", this.onMoveStart.bind( this ) ); + this.map.on( "moveend", this.onMoveEnd.bind( this ) ); + + this.view.on( "change:resolution", this.onChangeResolution.bind( this ) ); +}; + +netgis.MapOpenLayers.prototype.createDefaultLayers = function() +{ + this.editLayer = new ol.layer.Vector( { source: new ol.source.Vector( { features: [] } ), style: this.styleEdit.bind( this ), zIndex: this.editLayerID } ); + this.map.addLayer( this.editLayer ); + + this.editEventsOn(); +}; + +netgis.MapOpenLayers.prototype.editEventsOn = function() +{ + this.editLayer.getSource().on( "addfeature", this.onEditLayerAdd.bind( this ) ); + //this.editLayer.getSource().on( "changefeature", this.onEditLayerChange.bind( this ) ); //TODO: fired on feature style change? use only one style function with selected/hover states? + this.editLayer.getSource().on( "removefeature", this.onEditLayerRemove.bind( this ) ); +}; + +netgis.MapOpenLayers.prototype.editEventsOff = function() +{ + //NOTE: this doesn't work because OL does not allow removing all listeners and listener function ref is changed by binding + //NOTE: see this.editEventsCommit + + //this.editLayer.getSource().un( "addfeature"/*, this.onEditLayerAdd*/ ); + //this.editLayer.getSource().un( "changefeature", this.onEditLayerChange.bind( this ) ); //TODO: fired on feature style change? use only one style function with selected/hover states? + //this.editLayer.getSource().un( "removefeature"/*, this.onEditLayerRemove*/ ); +}; + +netgis.MapOpenLayers.prototype.createInteractions = function() +{ + // View + this.interactions[ netgis.Modes.VIEW ] = + [ + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.PANNING ] = this.interactions[ netgis.Modes.VIEW ]; + this.interactions[ netgis.Modes.ZOOMING_IN ] = this.interactions[ netgis.Modes.VIEW ]; + this.interactions[ netgis.Modes.ZOOMING_OUT ] = this.interactions[ netgis.Modes.VIEW ]; + + // Draw + this.interactions[ netgis.Modes.DRAW_POINTS ] = + [ + new ol.interaction.Draw( { type: "Point", source: this.editLayer.getSource(), style: this.styleSketch.bind( this ) } ), + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.DRAW_LINES ] = + [ + new ol.interaction.Draw( { type: "LineString", source: this.editLayer.getSource(), style: this.styleSketch.bind( this ) } ), + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.DRAW_POLYGONS ] = + [ + new ol.interaction.Draw( { type: "Polygon", source: this.editLayer.getSource(), style: this.styleSketch.bind( this ) } ), + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + // Edit + this.interactions[ netgis.Modes.CUT_FEATURE_BEGIN ] = + [ + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.CUT_FEATURE_DRAW ] = + [ + new ol.interaction.Draw( { type: "Polygon" /*, source: this.editLayer.getSource()*/, style: this.styleSketch.bind( this ) } ), + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.CUT_FEATURE_DRAW ][ 0 ].on( "drawend", this.onCutFeatureDrawEnd.bind( this ) ); + + this.interactions[ netgis.Modes.MODIFY_FEATURES ] = + [ + new ol.interaction.Modify( { source: this.editLayer.getSource(), deleteCondition: ol.events.condition.doubleClick, style: this.styleModify.bind( this ) } ), + //new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.MODIFY_FEATURES ][ 0 ].on( "modifyend", this.onModifyFeaturesEnd.bind( this ) ); + + this.interactions[ netgis.Modes.DELETE_FEATURES ] = + [ + //new ol.interaction.Select( { layers: [ this.editLayer ], addCondition: ol.events.condition.pointerMove } ), + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.BUFFER_FEATURE_BEGIN ] = + [ + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + this.interactions[ netgis.Modes.BUFFER_FEATURE_EDIT ] = + [ + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; + + // Snapping + this.snapFeatures = new ol.Collection(); + + // Search + this.interactions[ netgis.Modes.SEARCH_PLACE ] = + [ + new ol.interaction.DragPan(), + new ol.interaction.MouseWheelZoom() + ]; +}; + +netgis.MapOpenLayers.prototype.createLayer = function( data ) +{ + //TODO: refactor into createLayerWMS, createLayerWFS etc. + + switch ( data.type ) + { + case netgis.LayerTypes.XYZ: + { + var layer = new ol.layer.Tile + ( + { + source: new ol.source.XYZ + ( + { + url: data.url, + crossOrigin: "anonymous" + } + ) + } + ); + + return layer; + } + + case netgis.LayerTypes.OSM: + { + var layer = new ol.layer.Tile + ( + { + source: new ol.source.XYZ + ( + { + url: "https://{a-c}.tile.openstreetmap.de/{z}/{x}/{y}.png", + crossOrigin: "anonymous" + } + ) + } + ); + + return layer; + } + + case netgis.LayerTypes.WMS: + { + var params = + { + url: data.url, + params: + { + "LAYERS": data.name, + "FORMAT": "image/png", + "TRANSPARENT": "true", + "VERSION": "1.1.1" + }, + serverType: "mapserver", + crossOrigin: "anonymous", + hidpi: false + //ratio: 3.0 + }; + + // Authorization + if ( data.username && data.password ) + { + params.imageLoadFunction = function( image, src ) + { + var request = new XMLHttpRequest(); + request.open( "GET", src ); + request.setRequestHeader( "Authorization", "Basic " + window.btoa( data.username + ":" + data.password ) ); + + request.onload = function() + { + image.getImage().src = src; + }; + + request.send(); + }; + } + + var source = new ol.source.ImageWMS( params ); + + var layer = new ol.layer.Image + ( + { + source: source, + //zIndex: index, + opacity: 1.0 + } + ); + + return layer; + } + + case netgis.LayerTypes.WFS: + { + var config = this.client.config; + + var url = data.url + + "service=WFS" + + "&version=1.1.0" + + "&request=GetFeature" + + "&outputformat=application/json"; + + var source = new ol.source.Vector + ( + { + format: new ol.format.GeoJSON(), + strategy: ol.loadingstrategy.bbox, + url: function( extent ) + { + return url + + "&typename=" + data.name + + "&srsname=" + config.map.projection + + "&bbox=" + extent.join( "," ) + "," + config.map.projection; + } + } + ); + + var layer = new ol.layer.Vector + ( + { + source: source + } + ); + + var self = this; + source.on( "featuresloadstart", function( e ) { self.removeSnapLayer( layer ); } ); + source.on( "featuresloadend", function( e ) { window.setTimeout( function() { self.addSnapLayer( layer ); }, 10 ); } ); + + return layer; + } + } + + return null; +}; + +netgis.MapOpenLayers.prototype.clearAll = function() +{ + for ( var i = 0; i < this.layers.length; i++ ) + { + this.map.removeLayer( this.layers[ i ] ); + } + + this.layers = []; + + this.snapFeatures.clear(); +}; + +netgis.MapOpenLayers.prototype.onUpdateStyle = function( e ) +{ + var style = new ol.style.Style + ( + { + //image: new ol.style.Circle( { radius: 7, fill: new ol.style.Fill( { color: "#ff0000" } ) } ), + fill: new ol.style.Fill( { color: e.polygon.fill } ), + stroke: new ol.style.Stroke( { color: e.polygon.stroke, width: e.polygon.strokeWidth } ) + } + ); + + this.editLayer.setStyle( style ); +}; + +netgis.MapOpenLayers.prototype.styleEdit = function() +{ + var radius = this.client.config.styles.editLayer.pointRadius; + + var style = new ol.style.Style + ( + { + image: new ol.style.Circle( { radius: radius, fill: new ol.style.Fill( { color: this.client.config.styles.editLayer.stroke } ) } ), + fill: new ol.style.Fill( { color: this.client.config.styles.editLayer.fill } ), + stroke: new ol.style.Stroke( { color: this.client.config.styles.editLayer.stroke, width: this.client.config.styles.editLayer.strokeWidth } ) + } + ); + + return style; +}; + +netgis.MapOpenLayers.prototype.styleSelect = function( feature ) +{ + var style = new ol.style.Style + ( + { + image: new ol.style.Circle( { radius: this.client.config.styles.select.pointRadius, fill: new ol.style.Fill( { color: this.client.config.styles.select.stroke } ) } ), + fill: new ol.style.Fill( { color: this.client.config.styles.select.fill } ), + stroke: new ol.style.Stroke( { color: this.client.config.styles.select.stroke, width: this.client.config.styles.select.strokeWidth } ) + } + ); + + return style; +}; + +netgis.MapOpenLayers.prototype.styleModify = function( feature ) +{ + var style = new ol.style.Style + ( + { + image: new ol.style.Circle( { radius: this.client.config.styles.modify.pointRadius, fill: new ol.style.Fill( { color: this.client.config.styles.modify.stroke } ) } ), + fill: new ol.style.Fill( { color: this.client.config.styles.modify.fill } ), + stroke: new ol.style.Stroke( { color: this.client.config.styles.modify.stroke, width: this.client.config.styles.modify.strokeWidth } ) + } + ); + + var vertex = new ol.style.Style + ( + { + image: new ol.style.Circle( { radius: this.client.config.styles.modify.pointRadius, fill: new ol.style.Fill( { color: this.client.config.styles.modify.stroke } ) } ), + geometry: this.getGeometryPoints( feature ) + } + ); + + return [ style, vertex ]; +}; + +netgis.MapOpenLayers.prototype.styleSketch = function( feature ) +{ + var style = new ol.style.Style + ( + { + image: new ol.style.Circle( { radius: this.client.config.styles.sketch.pointRadius, fill: new ol.style.Fill( { color: this.client.config.styles.sketch.stroke } ) } ), + fill: new ol.style.Fill( { color: this.client.config.styles.sketch.fill } ), + stroke: new ol.style.Stroke( { color: this.client.config.styles.sketch.stroke, width: this.client.config.styles.sketch.strokeWidth } ) + } + ); + + var vertex = new ol.style.Style + ( + { + image: new ol.style.Circle( { radius: this.client.config.styles.sketch.pointRadius, fill: new ol.style.Fill( { color: this.client.config.styles.sketch.stroke } ) } ), + geometry: this.getGeometryPoints( feature ) + } + ); + + return [ style, vertex ]; +}; + +netgis.MapOpenLayers.prototype.getGeometryPoints = function( feature ) +{ + var geometry = feature.getGeometry(); + + if ( geometry instanceof ol.geom.LineString ) + { + return new ol.geom.MultiPoint( geometry.getCoordinates() ); + } + else if ( geometry instanceof ol.geom.Polygon ) + { + //return new ol.geom.MultiPoint( geometry.getCoordinates()[ 0 ] ); + + var points = []; + var geomCoords = geometry.getCoordinates(); + + for ( var g = 0; g < geomCoords.length; g++ ) + { + var coords = geomCoords[ g ]; + + for ( var c = 0; c < coords.length; c++ ) + points.push( coords[ c ] ); + } + + return new ol.geom.MultiPoint( points ); + } + else if ( geometry instanceof ol.geom.MultiPolygon ) + { + var points = []; + var polys = geometry.getPolygons(); + + for ( var l = 0; l < polys.length; l++ ) + { + var geomCoords = polys[ l ].getCoordinates(); + + for ( var g = 0; g < geomCoords.length; g++ ) + { + var coords = geomCoords[ g ]; + + for ( var c = 0; c < coords.length; c++ ) + points.push( coords[ c ] ); + } + } + + return new ol.geom.MultiPoint( points ); + } + else if ( geometry instanceof ol.geom.MultiLineString ) + { + var points = []; + var lines = geometry.getPolygons(); + + for ( var l = 0; l < lines.length; l++ ) + { + var geomCoords = lines[ l ].getCoordinates(); + + for ( var g = 0; g < geomCoords.length; g++ ) + { + var coords = geomCoords[ g ]; + + for ( var c = 0; c < coords.length; c++ ) + points.push( coords[ c ] ); + } + } + + return new ol.geom.MultiPoint( points ); + } + + return geometry; +}; + +netgis.MapOpenLayers.prototype.getActiveVectorLayers = function() +{ + var vectorLayers = []; + var mapLayers = this.map.getLayers().getArray(); + + var layers = this.layers; // this.map.getLayers().getArray(); + + for ( var i = 0; i < layers.length; i++ ) + { + //console.info( "Layer:", layers[ i ] ); + + var layer = layers[ i ]; + + if ( layer instanceof ol.layer.Vector && mapLayers.indexOf( layer ) > -1 ) + { + vectorLayers.push( layer ); + } + } + + return vectorLayers; +}; + +netgis.MapOpenLayers.prototype.setMode = function( mode ) +{ + // Old Mode + switch ( this.mode ) + { + case netgis.Modes.BUFFER_FEATURE_EDIT: + { + this.onBufferCancel( null ); + break; + } + } + + // Interactions + this.map.getInteractions().clear(); + + var interactions = this.interactions[ mode ]; + + if ( interactions ) + { + for ( var i = 0; i < interactions.length; i++ ) + { + this.map.addInteraction( interactions[ i ] ); + } + } + + if ( this.snap ) + { + if ( mode === netgis.Modes.DRAW_POINTS || mode === netgis.Modes.DRAW_LINES || mode === netgis.Modes.DRAW_POLYGONS ) + { + this.map.addInteraction( this.snap ); + } + } + + // Style + switch ( mode ) + { + default: + { + this.editLayer.setStyle( this.styleEdit.bind( this ) ); + break; + } + + case netgis.Modes.MODIFY_FEATURES: + { + this.editLayer.setStyle( this.styleModify.bind( this ) ); + break; + } + }; + + // Cursors + if ( this.mode ) this.root.classList.remove( this.getModeClassName( this.mode ) ); + if ( mode ) this.root.classList.add( this.getModeClassName( mode ) ); + + this.mode = mode; +}; + +netgis.MapOpenLayers.prototype.getModeClassName = function( mode ) +{ + var modeClass = mode.toLowerCase(); + //modeClass = modeClass.replace( "_", "-" ); + modeClass = netgis.util.replace( modeClass, "_", "-" ); + modeClass = "netgis-mode-" + modeClass; + + return modeClass; +}; + +netgis.MapOpenLayers.prototype.setSnapOn = function() +{ + //this.snapFeatures = new ol.Collection(); + this.snap = new ol.interaction.Snap( { features: this.snapFeatures } ); + this.map.addInteraction( this.snap ); + + this.snapFeatures.changed(); + + //this.updateSnapLayers(); +}; + +netgis.MapOpenLayers.prototype.setSnapOff = function() +{ + if ( this.snap ) + { + this.map.removeInteraction( this.snap ); + this.snap = null; + //this.snapFeatures = null; + } +}; +/* +netgis.MapOpenLayers.prototype.updateSnapLayers = function() +{ + var snapLayers = this.getActiveVectorLayers(); + + this.snapFeatures.clear(); + + if ( snapLayers.length > 0 ) + { + for ( var i = 0; i < snapLayers.length; i++ ) + { + var layerFeatures = snapLayers[ i ].getSource().getFeatures(); + + for ( var j = 0; j < layerFeatures.length; j++ ) + { + this.snapFeatures.push( layerFeatures[ j ] ); + } + } + + console.info( "Snap Features:", this.snapFeatures.getLength() ); + } +}; +*/ + +netgis.MapOpenLayers.prototype.addSnapLayer = function( vectorLayer ) +{ + var layerFeatures = vectorLayer.getSource().getFeatures(); + + for ( var j = 0; j < layerFeatures.length; j++ ) + { + this.snapFeatures.push( layerFeatures[ j ] ); + } +}; + +netgis.MapOpenLayers.prototype.removeSnapLayer = function( vectorLayer ) +{ + var layerFeatures = vectorLayer.getSource().getFeatures(); + + for ( var j = 0; j < layerFeatures.length; j++ ) + { + this.snapFeatures.remove( layerFeatures[ j ] ); + } +}; + +netgis.MapOpenLayers.prototype.onSnapOn = function( e ) +{ + this.setSnapOn(); +}; + +netgis.MapOpenLayers.prototype.onSnapOff = function( e ) +{ + this.setSnapOff(); +}; + +netgis.MapOpenLayers.prototype.onLayerShow = function( e ) +{ + var layer = this.layers[ e.id ]; + + if ( ! layer ) return; + + this.map.addLayer( layer ); + + //if ( /*this.snap &&*/ layer instanceof ol.layer.Vector ) this.addSnapLayer( layer ); //this.updateSnapLayers(); + + if ( layer instanceof ol.layer.Vector ) this.addSnapLayer( layer ); +}; + +netgis.MapOpenLayers.prototype.onLayerHide = function( e ) +{ + var layer = this.layers[ e.id ]; + + if ( ! layer ) return; + + this.map.removeLayer( layer ); + + if ( layer instanceof ol.layer.Vector ) this.removeSnapLayer( layer ); //this.updateSnapLayers(); +}; + +netgis.MapOpenLayers.prototype.onContextUpdate = function( e ) +{ + this.clearAll(); + + var context = e; + + // Bounding Box + var bbox = context.bbox; + + if ( bbox ) + { + var bbox1; + var bbox2; + + if ( netgis.util.isDefined( this.client.config.map ) && netgis.util.isDefined( this.client.config.map.projection ) ) + { + bbox1 = ol.proj.fromLonLat( [ bbox[ 0 ], bbox[ 1 ] ], this.client.config.map.projection ); + bbox2 = ol.proj.fromLonLat( [ bbox[ 2 ], bbox[ 3 ] ], this.client.config.map.projection ); + } + else + { + bbox1 = ol.proj.fromLonLat( [ bbox[ 0 ], bbox[ 1 ] ] ); + bbox2 = ol.proj.fromLonLat( [ bbox[ 2 ], bbox[ 3 ] ] ); + } + + bbox[ 0 ] = bbox1[ 0 ]; + bbox[ 1 ] = bbox1[ 1 ]; + bbox[ 2 ] = bbox2[ 0 ]; + bbox[ 3 ] = bbox2[ 1 ]; + + this.view.fit( bbox ); + } + + // Layers + //this.layers = []; + + for ( var l = 0; l < context.layers.length; l++ ) + //for ( var l = context.layers.length - 1; l >= 0; l-- ) + { + var data = context.layers[ l ]; + var layer = this.createLayer( data ); + + if ( layer ) + { + layer.setZIndex( context.layers.length - l ); + //this.layers.push( layer ); + this.layers[ l ] = layer; + } + } + + //this.map.getLayers().clear(); + + //TODO: active layers from context? + + // Active State + /*for ( var l = 0; l < context.layers.length; l++ ) + { + var data = context.layers[ l ]; + if ( data.active ) this.onLayerShow( { id: l } ); + }*/ +}; + +netgis.MapOpenLayers.prototype.onSetMode = function( e ) +{ + this.setMode( e ); +}; + +netgis.MapOpenLayers.prototype.onSetExtent = function( e ) +{ + var minxy = ol.proj.fromLonLat( [ e.minx, e.miny ], this.client.config.map.projection ); + var maxxy = ol.proj.fromLonLat( [ e.maxx, e.maxy ], this.client.config.map.projection ); + + this.view.fit( [ minxy[ 0 ], minxy[ 1 ], maxxy[ 0 ], maxxy[ 1 ] ] ); +}; + +netgis.MapOpenLayers.prototype.onChangeZoom = function( e ) +{ + var delta = e; + this.view.animate( { zoom: this.view.getZoom() + delta, duration: 200 } ); +}; + +netgis.MapOpenLayers.prototype.onPointerMove = function( e ) +{ + //var pixel = e.pixel; + + var hover = this.hover; + var styleSelect = this.styleSelect.bind( this ); + + if ( hover ) + { + hover.setStyle( this.styleEdit.bind( this ) ); + hover = null; + } + + var self = this; + + switch ( this.mode ) + { + case netgis.Modes.DELETE_FEATURES: + { + this.map.forEachFeatureAtPixel + ( + e.pixel, + function( feature, layer ) //TODO: bind to this? + { + if ( layer === self.editLayer ) + { + hover = feature; + feature.setStyle( styleSelect ); + } + + return true; + } + ); + + break; + } + + case netgis.Modes.CUT_FEATURE_BEGIN: + { + this.map.forEachFeatureAtPixel + ( + e.pixel, + function( feature, layer ) //TODO: bind to this? + { + if ( layer === self.editLayer ) + { + hover = feature; + feature.setStyle( styleSelect ); + } + + return true; + } + ); + + break; + } + + case netgis.Modes.BUFFER_FEATURE_BEGIN: + { + this.map.forEachFeatureAtPixel + ( + e.pixel, + function( feature, layer ) //TODO: bind to this? + { + if ( layer === self.editLayer ) + { + hover = feature; + feature.setStyle( styleSelect ); + } + + return true; + } + ); + + break; + } + } + + //TODO: refactor to default hover handler? + + this.hover = hover; +}; + +netgis.MapOpenLayers.prototype.onSingleClick = function( e ) +{ + switch ( this.mode ) + { + case netgis.Modes.DELETE_FEATURES: + { + if ( this.hover ) + { + this.editLayer.getSource().removeFeature( this.hover ); + this.hover = null; + } + + break; + } + + case netgis.Modes.CUT_FEATURE_BEGIN: + { + if ( this.hover ) + { + this.selected = this.hover; + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.CUT_FEATURE_DRAW ); + } + + break; + } + + case netgis.Modes.BUFFER_FEATURE_BEGIN: + { + if ( this.hover ) + { + this.selected = this.hover; + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.BUFFER_FEATURE_EDIT ); + } + + break; + } + } +}; + +netgis.MapOpenLayers.prototype.onMoveStart = function( e ) +{ + //TODO: problem with toolbars after head menu click + //this.client.invoke( netgis.Events.MAP_SET_MODE, netgis.MapModes.PANNING ); +}; + +netgis.MapOpenLayers.prototype.onMoveEnd = function( e ) +{ + //TODO: problem with toolbars after head menu click + //this.client.invoke( netgis.Events.MAP_SET_MODE, netgis.MapModes.VIEW ); +}; + +netgis.MapOpenLayers.prototype.onChangeResolution = function( e ) +{ + //var d = e.oldValue - this.view.getResolution(); + //this.client.invoke( netgis.Events.MAP_SET_MODE, ( d > 0.0 ) ? netgis.MapModes.ZOOMING_IN : netgis.MapModes.ZOOMING_OUT ); +}; + +netgis.MapOpenLayers.prototype.onCutFeatureDrawEnd = function( e ) +{ + var cutter = e.feature; + var target = this.selected; + + if ( target ) + { + // Cut Process + var parser = new jsts.io.OL3Parser(); + + var a = parser.read( target.getGeometry() ); + var b = parser.read( cutter.getGeometry() ); + + var c = a.difference( b ); + + // Output + var geom = parser.write( c ); + var feature = new ol.Feature( { geometry: geom } ); + + var source = this.editLayer.getSource(); + source.removeFeature( target ); + source.addFeature( feature ); + + this.selected = feature; + } + + this.editEventsSilent = true; + this.splitMultiPolygons( this.editLayer ); + this.editEventsSilent = false; + this.updateEditOutput(); +}; + +netgis.MapOpenLayers.prototype.onModifyFeaturesEnd = function( e ) +{ + this.updateEditOutput(); +}; + +netgis.MapOpenLayers.prototype.onBufferChange = function( e ) +{ + var radius = e.radius; + var segments = e.segments; + + var source = this.editLayer.getSource(); + var target = this.selected; + + if ( this.sketch ) + { + source.removeFeature( this.sketch ); + } + + if ( target ) + { + // Cut Process + var parser = new jsts.io.OL3Parser(); + + var a = parser.read( target.getGeometry() ); + var b = a.buffer( radius, segments ); + + // Output + var geom = parser.write( b ); + var feature = new ol.Feature( { geometry: geom } ); + + //source.removeFeature( target ); + source.addFeature( feature ); + + this.sketch = feature; + } +}; + +netgis.MapOpenLayers.prototype.onBufferAccept = function( e ) +{ + if ( this.selected && this.sketch ) + { + var source = this.editLayer.getSource(); + + // Delete Input Feature + //if ( ! ( this.selected.getGeometry() instanceof ol.geom.Point ) ) + source.removeFeature( this.selected ); + } + + this.sketch = null; + this.selected = null; +}; + +netgis.MapOpenLayers.prototype.onBufferCancel = function( e ) +{ + if ( this.sketch ) + { + this.editLayer.getSource().removeFeature( this.sketch ); + this.sketch = null; + } + + this.selected = null; +}; + +netgis.MapOpenLayers.prototype.onEditLayerAdd = function( e ) +{ + this.updateEditOutput(); + this.updateEditLayerItem(); + this.snapFeatures.push( e.feature ); +}; + +netgis.MapOpenLayers.prototype.onEditLayerRemove = function( e ) +{ + this.updateEditOutput(); + this.snapFeatures.remove( e.feature ); +}; + +netgis.MapOpenLayers.prototype.onEditLayerChange = function( e ) +{ + this.updateEditOutput(); +}; + +netgis.MapOpenLayers.prototype.updateEditOutput = function() +{ + var features = this.editLayer.getSource().getFeatures(); + + // Output + var format = new ol.format.GeoJSON(); + //var output = format.writeFeatures( features ); + var output = format.writeFeaturesObject( features ); + + if ( ! this.editEventsSilent ) + this.client.invoke( netgis.Events.EDIT_FEATURES_CHANGE, output ); +}; + +netgis.MapOpenLayers.prototype.updateEditLayerItem = function() +{ + // Create layer item if not existing + var id = this.editLayerID; + + if ( ! this.layers[ id ] ) + { + this.layers[ id ] = this.editLayer; + this.client.invoke( netgis.Events.LAYER_CREATED, { id: id, title: "Zeichnung", checked: true, folder: "draw" } ); + } +}; + +netgis.MapOpenLayers.prototype.onEditFeaturesLoaded = function( e ) +{ + var json = e; + var format = new ol.format.GeoJSON(); + var features = format.readFeatures( json ); + + this.editLayer.getSource().addFeatures( features ); + //this.snapFeatures.push( e.feature ); + + if ( features.length > 0 ) + this.view.fit( this.editLayer.getSource().getExtent(), { padding: [ 40, 40, 40, 40 ] } ); +}; + +netgis.MapOpenLayers.prototype.onDragEnter = function( e ) +{ + e.preventDefault(); + + this.dropTarget.classList.remove( "netgis-hide" ); + + //TODO: refactor into dragdrop module + events ? + + return false; +}; + +netgis.MapOpenLayers.prototype.onDragLeave = function( e ) +{ + this.dropTarget.classList.add( "netgis-hide" ); + + return false; +}; + +netgis.MapOpenLayers.prototype.onDragDrop = function( e ) +{ + console.info( "Drag Drop" ); + + this.dropTarget.classList.add( "netgis-hide" ); + + e.preventDefault(); + + var file = e.dataTransfer.files[ 0 ]; + var reader = new FileReader(); + + reader.onload = this.onDragLoad.bind( this ); + + console.log( "File:", file ); + + //reader.readAsDataURL( file ); + reader.readAsArrayBuffer( file ); + + return false; +}; + +netgis.MapOpenLayers.prototype.onDragLoad = function( e ) +{ + console.log( "On Load:", e.target ); + + this.loadShape( e.target.result ); +}; + +netgis.MapOpenLayers.prototype.loadShape = function( data ) +{ + var self = this; + + shp( data ).then + ( + function( geojson ) + { + self.onShapeLoad( geojson ); + } + ); +}; + +netgis.MapOpenLayers.prototype.onShapeLoad = function( geojson ) +{ + console.info( "Shapefile To Geojson:", geojson ); + + var features = new ol.format.GeoJSON( { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" } ).readFeatures( geojson ); + this.importLayer.getSource().addFeatures( features ); + + this.view.fit( this.importLayer.getSource().getExtent(), {} ); +}; + +netgis.MapOpenLayers.prototype.onImportGeoJSON = function( e ) +{ + var file = e; + var title = file.name; + var self = this; + + var reader = new FileReader(); + reader.onload = function( e ) { self.createLayerGeoJSON( title, e.target.result ); }; + reader.readAsText( file ); +}; + +netgis.MapOpenLayers.prototype.onImportGML = function( e ) +{ + var file = e; + var title = file.name; + var self = this; + + var reader = new FileReader(); + reader.onload = function( e ) { self.createLayerGML( title, e.target.result ); }; + reader.readAsText( file ); +}; + +netgis.MapOpenLayers.prototype.onImportShapefile = function( e ) +{ + var file = e; + var title = file.name; + var self = this; + + var reader = new FileReader(); + reader.onload = function( e ) { self.createLayerShapefile( title, e.target.result ); }; + reader.readAsArrayBuffer( file ); +}; + +netgis.MapOpenLayers.prototype.createLayerGeoJSON = function( title, data ) +{ + //var format = new ol.format.GeoJSON( { dataProjection: "EPSG:4326", featureProjection: this.client.config.map.projection /*"EPSG:3857"*/ } ); + var format = new ol.format.GeoJSON(); + var projection = format.readProjection( data ); + var features = format.readFeatures( data, { featureProjection: this.client.config.map.projection } ); + + //NOTE: proj4.defs[ "EPSG:4326" ] + //NOTE: netgis.util.foreach( proj4.defs, function( k,v ) { console.info( "DEF:", k, v ); } ) + + //console.info( "Projection:", projection.getCode() ); + + this.addImportedFeatures( features ); +}; + +netgis.MapOpenLayers.prototype.createLayerGML = function( title, data ) +{ + //NOTE: https://stackoverflow.com/questions/35935184/opening-qgis-exported-gml-in-openlayers-3 + //NOTE: https://github.com/openlayers/openlayers/issues/5023 + + console.warn( "GML support is experimental!" ); + + var format = new ol.format.WFS( /*{ srsName: "EPSG:4326", featureType: "ogr:RLP_OG_utf8_epsg4326" }*/ ); + //var format = new ol.format.GML( { featureNS: "ogr", featureType: "ogr:RLP_OG_utf8_epsg4326" } ); + //var format = new ol.format.WFS(); + //var format = new ol.format.WFS( { featureNS: "ogr", featureType: "RLP_OG_utf8_epsg4326" } ); + var projection = format.readProjection( data ); + //var features = format.readFeatures( data, { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" } ); + var features = format.readFeatures( data, { featureProjection: this.client.config.map.projection } ); + + console.info( "GML:", projection, features ); + + this.addImportedFeatures( features ); +}; + +netgis.MapOpenLayers.prototype.createLayerShapefile = function( title, shapeData ) +{ + var self = this; + + shp( shapeData ).then + ( + function( geojson ) + { + //var format = new ol.format.GeoJSON( { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" } ); + var format = new ol.format.GeoJSON(); + var projection = format.readProjection( geojson ); + var features = format.readFeatures( geojson, { featureProjection: self.client.config.map.projection } ); + + self.addImportedFeatures( features ); + } + ); +}; + +netgis.MapOpenLayers.prototype.addImportedFeatures = function( features ) +{ + // Add To Edit Layer + this.editEventsSilent = true; + this.editLayer.getSource().addFeatures( features ); + this.editEventsSilent = false; + this.updateEditOutput(); + + /* + // Create New Layer + var id = this.importLayerID; + this.importLayerID += 1; + + var layer = new ol.layer.Vector( { source: new ol.source.Vector( { features: features } ), zIndex: id } ); + this.map.addLayer( layer ); + this.layers[ id ] = layer; + this.addSnapLayer( layer ); + + if ( features.length > 0 ) + this.view.fit( layer.getSource().getExtent(), {} ); + + this.client.invoke( netgis.Events.LAYER_CREATED, { id: id, title: title, checked: true, folder: "import" } ); + */ +}; + +netgis.MapOpenLayers.prototype.onExportPDF = function( e ) +{ + this.exportImage( "pdf", e.resx, e.resy, e.mode, e.margin ); +}; + +netgis.MapOpenLayers.prototype.onExportJPEG = function( e ) +{ + this.exportImage( "jpeg", e.resx, e.resy ); +}; + +netgis.MapOpenLayers.prototype.onExportPNG = function( e ) +{ + this.exportImage( "png", e.resx, e.resy ); +}; + +netgis.MapOpenLayers.prototype.onExportGIF = function( e ) +{ + this.exportImage( "gif", e.resx, e.resy ); +}; + +netgis.MapOpenLayers.prototype.getWidth = function() +{ + return this.map.getSize()[ 0 ]; +}; + +netgis.MapOpenLayers.prototype.getHeight = function() +{ + return this.map.getSize()[ 1 ]; +}; + +/** +* +* @param {format} string Format identifier (jpeg, png, gif) +* @param {resx} integer Map image x resolution (pixels) +* @param {resy} integer Map image y resolution (pixels) +* @param {mode} boolean PDF mode (true = landscape, false = portrait) +* @param {margin} integer PDF page margin (millimeters) +*/ +netgis.MapOpenLayers.prototype.exportImage = function( format, resx, resy, mode, margin ) +{ + this.client.invoke( netgis.Events.EXPORT_BEGIN, null ); + + var self = this; + var root = this.root; + var map = this.map; + var config = this.client.config; + + // Request Logo Image + var logo = new Image(); + logo.onload = function() + { + //TODO: refactor map render image and image export + //NOTE: https://github.com/openlayers/openlayers/issues/9100 + //NOTE: scaling / quality bugs when map pixel ratio is not 1.0 + + // Render Target + var renderContainer = document.createElement( "div" ); + renderContainer.style.position = "fixed"; + renderContainer.style.top = "0px"; + renderContainer.style.left = "0px"; + renderContainer.style.width = resx + "px"; + renderContainer.style.height = resy + "px"; + renderContainer.style.background = "white"; + renderContainer.style.zIndex = -1; + renderContainer.style.opacity = 0.0; + renderContainer.style.pointerEvents = "none"; + root.appendChild( renderContainer ); + + map.setTarget( renderContainer ); + + // Request Render + map.once + ( + "rendercomplete", + function() + { + var mapCanvas = document.createElement( "canvas" ); + mapCanvas.width = resx; + mapCanvas.height = resy; + + var mapContext = mapCanvas.getContext( "2d" ); + mapContext.webkitImageSmoothingEnabled = false; + mapContext.mozImageSmoothingEnabled = false; + mapContext.imageSmoothingEnabled = false; + + // Loop Map Layers + Array.prototype.forEach.call + ( + document.querySelectorAll( ".ol-layer canvas" ), + function( canvas ) + { + if ( canvas.width > 0 ) + { + var opacity = canvas.parentNode.style.opacity; + mapContext.globalAlpha = ( opacity === '' ) ? 1.0 : Number( opacity ); + + var transform = canvas.style.transform; + var matrix = transform.match( /^matrix\(([^\(]*)\)$/ )[ 1 ].split( "," ).map( Number ); + + CanvasRenderingContext2D.prototype.setTransform.apply( mapContext, matrix ); + + mapContext.drawImage( canvas, 0, 0 ); + } + } + ); + + // Watermark Logo + mapContext.drawImage( logo, 0, 0 ); + + // Export Map Image + var link = document.createElement( "a" ); + + switch ( format ) + { + case "pdf": + { + // Dimensions + var landscape = mode; + margin = margin ? margin : 0; + var widthA4 = 297 - margin - margin; + var heightA4 = 210 - margin - margin; + var ratio = mapCanvas.width / mapCanvas.height; + + if ( ! landscape ) + { + var w = widthA4; + widthA4 = heightA4; + heightA4 = w; + } + + var width; + var height; + + if ( mapCanvas.height > mapCanvas.width ) + { + // Tall Canvas + height = heightA4; + width = height * ratio; + + if ( width > widthA4 ) + { + width = widthA4; + height = width / ratio; + } + } + else + { + // Wide Canvas + width = widthA4; + height = width / ratio; + + if ( height > heightA4 ) + { + height = heightA4; + width = height * ratio; + } + } + + var pdf = new jsPDF( landscape ? "l" : "p" ); + + var x = margin; + x += ( widthA4 - width ) / 2; + + var y = margin; + y += ( heightA4 - height ) / 2; + + // Map Image + pdf.addImage( mapCanvas.toDataURL( "image/png,1.0", 1.0 ), "PNG", x, y, width, height ); + + // Text + pdf.setFontSize( 8 ); + pdf.text( "Quelle: " + window.location.href, x + 2, y + height - 2 ); + + // Same Tab + //pdf.output( "save", { filename: config.export.defaultFilename + ".pdf" } ); + + // New Tab (without Name) + var data = pdf.output( "bloburl", { filename: config.export.defaultFilename + ".pdf" } ); + window.open( data, "_blank" ); + + /* + // Download (with Name) + var data = pdf.output( "blob", { filename: config.export.defaultFilename + ".pdf" } ); + var blob = new Blob( [ data ], { type: "octet/stream" } ); + link.setAttribute( "download", "Export.pdf" ); + link.setAttribute( "href", window.URL.createObjectURL( blob ) ); + link.click(); + //window.URL.revokeObjectURL( url ); + */ + + break; + } + + case "jpeg": + { + if ( window.navigator.msSaveBlob ) + { + window.navigator.msSaveBlob( mapCanvas.msToBlob(), config.export.defaultFilename + ".jpg" ); + } + else + { + link.setAttribute( "download", config.export.defaultFilename + ".jpg" ); + link.setAttribute( "href", mapCanvas.toDataURL( "image/jpeg", 1.0 ) ); + link.click(); + } + + break; + } + + case "png": + { + if ( window.navigator.msSaveBlob ) + { + //if ( ! config.export.openNewTab ) + window.navigator.msSaveBlob( mapCanvas.msToBlob(), config.export.defaultFilename + ".png" ); + /*else + window.open( mapCanvas.msToBlob(), "_blank" );*/ + } + else + { + /*if ( ! config.export.openNewTab ) + {*/ + link.setAttribute( "download", config.export.defaultFilename + ".png" ); + link.setAttribute( "href", mapCanvas.toDataURL( "image/png", 1.0 ) ); + link.click(); + /*} + else + window.open( mapCanvas.toDataURL( "image/png", 1.0 ), "_blank" );*/ + } + + break; + } + + case "gif": + { + link.setAttribute( "download", config.export.defaultFilename + ".gif" ); + + var gif = new GIF( { workerScript: config.export.gifWebWorker, quality: 1 } ); + gif.addFrame( mapCanvas ); + + gif.on + ( + "finished", + function( blob ) + { + link.setAttribute( "href", window.URL.createObjectURL( blob ) ); + link.click(); + } + ); + + gif.render(); + + break; + } + } + + /// Done + map.setTarget( root ); + root.removeChild( renderContainer ); + + self.client.invoke( netgis.Events.EXPORT_END, null ); + } + ); + + // Begin Map Render + map.renderSync(); + }; + + // Begin Logo Load & Render + logo.src = config.export.logo; +}; + +netgis.MapOpenLayers.prototype.splitMultiPolygons = function( layer ) +{ + //TODO: split only selected feature ( parameter ) + + var source = layer.getSource(); + var features = source.getFeatures(); + + var removeFeatures = []; + var newFeatures = []; + + // Find Multi Features + for ( var i = 0; i < features.length; i++ ) + { + var feature = features[ i ]; + var geom = feature.getGeometry(); + + if ( geom instanceof ol.geom.MultiPolygon ) + { + var polygons = geom.getPolygons(); + + // Create Single Features + for ( var j = 0; j < polygons.length; j++ ) + { + var polygon = polygons[ j ]; + var newFeature = new ol.Feature( { geometry: polygon } ); + newFeatures.push( newFeature ); + } + + removeFeatures.push( feature ); + } + } + + // Remove Multi Features + for ( var i = 0; i < removeFeatures.length; i++ ) + { + source.removeFeature( removeFeatures[ i ] ); + } + + // Add Single Features + source.addFeatures( newFeatures ); +}; \ No newline at end of file diff --git a/src/netgis/Menu.css b/src/netgis/Menu.css new file mode 100644 index 0000000..ffc24de --- /dev/null +++ b/src/netgis/Menu.css @@ -0,0 +1,118 @@ +.netgis-menu +{ + position: absolute; + left: 0mm; + right: 0mm; + top: 0mm; + height: 12mm; + /*min-height: 12mm;*/ + + /*min-height: 12mm;*/ + line-height: 12mm; + /*overflow-x: auto; + overflow-y: hidden;*/ + + white-space: nowrap; + font-size: 0mm; + + z-index: 1000; + + background: lightsalmon; +} + +.netgis-menu > div +{ + height: 12mm; + /*min-height: 12mm;*/ + white-space: nowrap; + /*font-size: 0mm;*/ +} + +/*.netgis-menu > * */ +.netgis-menu > div > * +{ + display: inline-block; + height: 100%; + /*font-size: 4mm;*/ + margin: 0mm; + /*padding: 0mm 4mm;*/ + /*float: left;*/ + + font-size: 4mm; +} + +.netgis-menu .netgis-right +{ + float: right; + padding-right: 0mm; + padding-left: 4mm; +} + +.netgis-menu span +{ + padding: 0mm 4mm 0mm 0mm; +} + +.netgis-menu button i +{ + font-size: 5mm; + width: 12mm; + line-height: 12mm; + text-align: center; +} + +.netgis-dropdown +{ + position: relative; + padding: 0mm; + /*bottom: 2px;*/ +} + +.netgis-dropdown .netgis-dropdown-content +{ + display: none; + /*position: fixed;*/ + position: absolute; + /*top: 12mm;*/ + min-width: 100%; + /*min-width: 40mm;*/ + padding: 0mm; + margin: 0mm; + margin-top: -1px; + z-index: 1; + + font-size: 4mm; + list-style-type: none; +} + +/*.netgis-dropdown:hover +{ + z-index: 10; +}*/ + +.netgis-dropdown:hover .netgis-dropdown-content +{ + display: block; +} + +.netgis-dropdown .netgis-dropdown-content i +{ + font-size: 4mm; + color: #bbb; +} + +.netgis-dropdown button +{ + width: 100%; + /* padding: 0mm 4mm; */ + padding: 0mm 4mm 0mm 0mm; + white-space: nowrap; + text-align: left; +} + +/*.netgis-dropdown i +{ + width: 12mm; + line-height: 12mm; + text-align: center; +}*/ diff --git a/src/netgis/Menu.js b/src/netgis/Menu.js new file mode 100644 index 0000000..93b4018 --- /dev/null +++ b/src/netgis/Menu.js @@ -0,0 +1,240 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.Menu = function() +{ + this.client = null; + this.root = null; + this.sections = []; +}; + +netgis.Menu.prototype.load = function() +{ + this.root = document.createElement( "header" ); + this.root.className = "netgis-menu netgis-primary netgis-shadow"; + + //TODO: refactor into abstract bar class (see toolbar) ? + var wrapper = document.createElement( "div" ); + this.root.appendChild( wrapper ); + + var toggle = this.createButton( 'Ebenen', true ); + toggle.addEventListener( "click", this.onToggleClick.bind( this ) ); + ////this.root.appendChild( toggle ); + wrapper.appendChild( toggle ); + + var search = this.createButton( 'Suche', true ); + search.addEventListener( "click", this.onSearchPlaceClick.bind( this ) ); + ////this.root.appendChild( search ); + wrapper.appendChild( search ); + + /*var title = this.createButton( 'GeoPortal', false ); + title.classList.remove( "netgis-hover-primary" ); //TODO: createText function? + //title.style.padding = "0mm"; + this.root.appendChild( title );*/ + + if ( this.client.editable ) + { + // Draw Menu + var draw = this.createMenu( 'Zeichnen' ); + var drawItems = draw.getElementsByTagName( "ul" )[ 0 ]; + ////this.root.appendChild( draw ); + wrapper.appendChild( draw ); + + drawItems.appendChild( this.createMenuItem( 'Punkte', this.onDrawPointClick.bind( this ) ) ); + drawItems.appendChild( this.createMenuItem( 'Linien', this.onDrawLineClick.bind( this ) ) ); + drawItems.appendChild( this.createMenuItem( 'Polygone', this.onDrawPolygonClick.bind( this ) ) ); + + // Edit Menu + var edit = this.createMenu( 'Bearbeiten' ); + var editItems = edit.getElementsByTagName( "ul" )[ 0 ]; + ////this.root.appendChild( edit ); + wrapper.appendChild( edit ); + + editItems.appendChild( this.createMenuItem( 'Ausschneiden', this.onCutFeatureClick.bind( this ) ) ); + editItems.appendChild( this.createMenuItem( 'Verschieben', this.onModifyFeaturesClick.bind( this ) ) ); + editItems.appendChild( this.createMenuItem( 'Löschen', this.onDeleteFeaturesClick.bind( this ) ) ); + editItems.appendChild( this.createMenuItem( 'Puffern', this.onBufferFeatureClick.bind( this ) ) ); + + // Import Menu + var importMenu = this.createMenu( 'Import' ); + //this.root.appendChild( importMenu ); + wrapper.appendChild( importMenu ); + + //fileItems.appendChild( this.createMenuItem( 'OWS-Context', this.onImportOWSClick.bind( this ) ) ); + + var importItem = importMenu.getElementsByTagName( "ul" )[ 0 ]; + importItem.appendChild( this.createMenuItem( 'GeoJSON', this.onImportGeoJSONClick.bind( this ) ) ); + //fileItems.appendChild( this.createMenuItem( 'KML', this.onImportKMLClick.bind( this ) ) ); + importItem.appendChild( this.createMenuItem( 'GML', this.onImportGMLClick.bind( this ) ) ); + importItem.appendChild( this.createMenuItem( 'Shapefile', this.onImportShapefileClick.bind( this ) ) ); + + // Export + var exportMenu = this.createMenu( 'Export' ); + ////this.root.appendChild( exportMenu ); + wrapper.appendChild( exportMenu ); + + var exportItems = exportMenu.getElementsByTagName( "ul" )[ 0 ]; + exportItems.appendChild( this.createMenuItem( 'PDF', this.onExportPDFClick.bind( this ) ) ); + exportItems.appendChild( this.createMenuItem( 'JPEG', this.onExportJPEGClick.bind( this ) ) ); + exportItems.appendChild( this.createMenuItem( 'PNG', this.onExportPNGClick.bind( this ) ) ); + exportItems.appendChild( this.createMenuItem( 'GIF', this.onExportGIFClick.bind( this ) ) ); + } + + // Search Menu + /*var search = this.createMenu( 'Suche' ); + var searchItems = search.getElementsByTagName( "ul" )[ 0 ]; + this.root.appendChild( search ); + + searchItems.appendChild( this.createMenuItem( 'Ortssuche', this.onSearchPlaceClick.bind( this ) ) ); + searchItems.appendChild( this.createMenuItem( 'Datensuche', this.onSearchDataClick.bind( this ) ) );*/ + + // Right Buttons + /* + var settings = this.createButton( '', true ); + this.root.appendChild( settings ); + + var help = this.createButton( '', true ); + this.root.appendChild( help ); + */ + + this.client.root.appendChild( this.root ); +}; + +netgis.Menu.prototype.createButton = function( label, right ) +{ + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.className = "netgis-primary netgis-hover-primary"; + if ( right ) button.className += " netgis-right"; + button.innerHTML = label; + + return button; +}; + +netgis.Menu.prototype.createMenu = function( title ) +{ + var menu = document.createElement( "div" ); + menu.className = "netgis-dropdown"; + + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.className = "netgis-primary netgis-hover-primary"; + button.innerHTML = title; + menu.appendChild( button ); + + var content = document.createElement( "ul" ); + content.className = "netgis-dropdown-content netgis-dialog netgis-shadow"; + menu.appendChild( content ); + + return menu; +}; + +netgis.Menu.prototype.createMenuItem = function( title, callback ) +{ + var item = document.createElement( "li" ); + item.className = "netgis-hover-light"; + + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.innerHTML = title; + button.addEventListener( "click", callback ); + item.appendChild( button ); + + return item; +}; + +netgis.Menu.prototype.onToggleClick = function( e ) +{ + this.client.invoke( netgis.Events.LAYER_LIST_TOGGLE, null ); +}; + +netgis.Menu.prototype.onDrawPointClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.DRAW_POINTS ); +}; + +netgis.Menu.prototype.onDrawLineClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.DRAW_LINES ); +}; + +netgis.Menu.prototype.onDrawPolygonClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.DRAW_POLYGONS ); +}; + +netgis.Menu.prototype.onCutFeatureClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.CUT_FEATURE_BEGIN ); +}; + +netgis.Menu.prototype.onModifyFeaturesClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.MODIFY_FEATURES ); +}; + +netgis.Menu.prototype.onDeleteFeaturesClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.DELETE_FEATURES ); +}; + +netgis.Menu.prototype.onBufferFeatureClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.BUFFER_FEATURE_BEGIN ); +}; + +netgis.Menu.prototype.onSearchPlaceClick = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.SEARCH_PLACE ); +}; + +netgis.Menu.prototype.onSearchDataClick = function( e ) +{ + alert( "TODO: data search interface" ); +}; + +netgis.Menu.prototype.onImportOWSClick = function( e ) +{ + alert( "TODO: ows import interface, try setting url parameter '?ows='" ); +}; + +netgis.Menu.prototype.onImportShapefileClick = function( e ) +{ + this.client.invoke( netgis.Events.IMPORT_SHAPEFILE_SHOW, null ); +}; + +netgis.Menu.prototype.onImportGeoJSONClick = function( e ) +{ + this.client.invoke( netgis.Events.IMPORT_GEOJSON_SHOW, null ); +}; + +netgis.Menu.prototype.onImportKMLClick = function( e ) +{ + alert( "TODO: kml import interface" ); +}; + +netgis.Menu.prototype.onImportGMLClick = function( e ) +{ + this.client.invoke( netgis.Events.IMPORT_GML_SHOW, null ); +}; + +netgis.Menu.prototype.onExportPDFClick = function( e ) +{ + this.client.invoke( netgis.Events.EXPORT_PDF_SHOW, null ); +}; + +netgis.Menu.prototype.onExportJPEGClick = function( e ) +{ + this.client.invoke( netgis.Events.EXPORT_JPEG_SHOW, null ); +}; + +netgis.Menu.prototype.onExportPNGClick = function( e ) +{ + this.client.invoke( netgis.Events.EXPORT_PNG_SHOW, null ); +}; + +netgis.Menu.prototype.onExportGIFClick = function( e ) +{ + this.client.invoke( netgis.Events.EXPORT_GIF_SHOW, null ); +}; \ No newline at end of file diff --git a/src/netgis/Modal.css b/src/netgis/Modal.css new file mode 100644 index 0000000..ec4d3ca --- /dev/null +++ b/src/netgis/Modal.css @@ -0,0 +1,160 @@ +.netgis-modal +{ + display: none; + + position: absolute; + width: 100%; + height: 100%; + top: 0mm; + left: 0mm; + padding: 6mm; + z-index: 5000; + + background: rgba( 0, 0, 0, 0.6 ); +} + +.netgis-modal.netgis-show +{ + display: block; +} + +.netgis-modal .netgis-dialog +{ + display: none; + + max-width: 160mm; + max-height: 160mm; + margin: auto; + + border-radius: 1mm; + border: 1mm solid #a7233f; + + overflow: hidden; + overflow-y: auto; + + cursor: default; + + /*transform: scale( 0.0 ); + transition: transform 2000ms ease;*/ +} + +.netgis-modal .netgis-dialog.netgis-show +{ + display: block; + + /*transform: scale( 1.0 ); + transition: transform 2000ms ease;*/ +} + +.netgis-modal header +{ + /*position: absolute;*/ + width: 100%; + height: 12mm; + /*left: 0mm; + top: 0mm;*/ + line-height: 12mm; + text-align: center; +} + +.netgis-modal header button +{ + position: relative; + width: 100%; + min-height: 12mm; +} + +.netgis-modal header button span +{ + display: block; + position: absolute; + right: 0mm; + top: 0mm; + line-height: 12mm; + padding: 0mm 4mm; + text-align: center; +} + +.netgis-modal-content +{ + /*position: absolute; + left: 4mm; + right: 4mm; + top: 16mm; + bottom: 4mm;*/ + padding: 6mm; + overflow: auto; +} + +.netgis-modal-content > table +{ + width: 100%; + border: none; + border-spacing: 0mm; +} + +.netgis-modal-content > table td, +.netgis-modal-content > table th +{ + width: 50%; + overflow: hidden; + vertical-align: top; + text-align: left; +} + +.netgis-modal-content .netgis-padding +{ + padding: 4mm 4mm; +} + +.netgis-modal-content .netgis-space +{ + height: 6mm; +} + +.netgis-modal-content button, +.netgis-modal-content label +{ + width: 100%; + min-height: 12mm; + /*line-height: 12mm;*/ +} + +.netgis-modal-content button i +{ + margin-right: 4mm; +} + +.netgis-modal-content button[disabled] +{ + opacity: 0.5; + cursor: not-allowed; +} + +.netgis-modal-content input +{ + width: 100%; +} + +.netgis-modal-content input[type=file] +{ + cursor: pointer; +} + +.netgis-modal-content ul +{ + margin: 0mm; + padding: 0mm; + padding-left: 4mm; + list-style-type: square; +} + +.netgis-modal-content li:not( :last-child ) +{ + margin-bottom: 4mm; +} + +.netgis-modal h3 +{ + margin: 0mm; +} diff --git a/src/netgis/Modal.js b/src/netgis/Modal.js new file mode 100644 index 0000000..5d8be16 --- /dev/null +++ b/src/netgis/Modal.js @@ -0,0 +1,592 @@ +"use strict"; + +var netgis = netgis || {}; + +/** + * Modal Module + * + * Notes: + * - Putting all modals in here for ease of implementation + * - This may be split up into one class for each modal (?) + * + * @returns {netgis.Modal} + */ +netgis.Modal = function() +{ + this.client = null; + this.importGeoJSON = null; + this.importGML = null; + this.importShapefile = null; + this.exportPDF = null; + this.exportJPEG = null; + this.exportPNG = null; + this.exportGIF = null; +}; + +netgis.Modal.prototype.load = function() +{ + this.root = document.createElement( "section" ); + this.root.className = "netgis-modal"; + this.root.addEventListener( "click", this.onRootClick.bind( this ) ); + + // Import + this.importGeoJSON = this.createImportGeoJSON(); + this.root.appendChild( this.importGeoJSON ); + + this.importGML = this.createImportGML(); + this.root.appendChild( this.importGML ); + + this.importShapefile = this.createImportShapefile(); + this.root.appendChild( this.importShapefile ); + + // Export + this.exportPDF = this.createExportPDF(); + this.root.appendChild( this.exportPDF ); + + this.exportJPEG = this.createExportJPEG(); + this.root.appendChild( this.exportJPEG ); + + this.exportPNG = this.createExportPNG(); + this.root.appendChild( this.exportPNG ); + + this.exportGIF = this.createExportGIF(); + this.root.appendChild( this.exportGIF ); + + // Done + this.client.root.appendChild( this.root ); + + // Events + this.client.on( netgis.Events.IMPORT_GEOJSON_SHOW, this.onImportGeoJSONShow.bind( this ) ); + this.client.on( netgis.Events.IMPORT_GML_SHOW, this.onImportGMLShow.bind( this ) ); + this.client.on( netgis.Events.IMPORT_SHAPEFILE_SHOW, this.onImportShapefileShow.bind( this ) ); + + this.client.on( netgis.Events.EXPORT_PDF_SHOW, this.onExportPDFShow.bind( this ) ); + this.client.on( netgis.Events.EXPORT_JPEG_SHOW, this.onExportJPEGShow.bind( this ) ); + this.client.on( netgis.Events.EXPORT_PNG_SHOW, this.onExportPNGShow.bind( this ) ); + this.client.on( netgis.Events.EXPORT_GIF_SHOW, this.onExportGIFShow.bind( this ) ); +}; + +netgis.Modal.prototype.createImportGeoJSON = function() +{ + var container = this.createContainer( "Import GeoJSON" ); + + this.createText( container, "Unterstützte Koordinatensysteme:", "
  • World Geodetic System 1984 (EPSG:4326)
  • ETRS89 / UTM zone 32N (EPSG:25832)
" ); + this.createInputFile( container, "Datei auswählen / ablegen:", ".geojson,.json", this.onImportGeoJSONChange.bind( this ) ); + this.createSpace( container ); + this.createButton( container, "Importieren", this.onImportGeoJSONAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.createImportGML = function() +{ + var container = this.createContainer( "Import GML" ); + + this.createText( container, "Unterstützte Koordinatensysteme:", "
  • World Geodetic System 1984 (EPSG:4326)
  • ETRS89 / UTM zone 32N (EPSG:25832)
" ); + this.createInputFile( container, "Datei auswählen / ablegen:", ".gml,.xml", this.onImportGMLChange.bind( this ) ); + this.createSpace( container ); + this.createButton( container, "Importieren", this.onImportGMLAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.createImportShapefile = function() +{ + var container = this.createContainer( "Import Shapefile" ); + + this.createText( container, "Unterstützte Koordinatensysteme:", "
  • World Geodetic System 1984 (EPSG:4326)
  • ETRS89 / UTM zone 32N (EPSG:25832)
" ); + this.createInputFile( container, "Datei auswählen / ablegen:", "application/zip", this.onImportShapefileChange.bind( this ) ); + this.createSpace( container ); + this.createButton( container, "Importieren", this.onImportShapefileAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.createExportPDF = function() +{ + var container = this.createContainer( "Export PDF" ); + + this.createInputInteger( container, "Breite (Pixel):", 800, 0, 4096 ); + this.createInputInteger( container, "Höhe (Pixel):", 600, 0, 4096 ); + this.createInputInteger( container, "Seitenränder (Millimeter):", 10, 0, 100 ); + this.createInputCheckbox( container, "Querformat:", true ); + this.createSpace( container ); + this.createButton( container, "Exportieren", this.onExportPDFAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.createExportJPEG = function() +{ + var container = this.createContainer( "Export JPEG" ); + + this.createInputInteger( container, "Breite (Pixel):", 800, 0, 4096 ); + this.createInputInteger( container, "Höhe (Pixel):", 600, 0, 4096 ); + this.createSpace( container ); + this.createButton( container, "Exportieren", this.onExportJPEGAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.createExportPNG = function() +{ + var container = this.createContainer( "Export PNG" ); + + this.createInputInteger( container, "Breite (Pixel):", 800, 0, 4096 ); + this.createInputInteger( container, "Höhe (Pixel):", 600, 0, 4096 ); + this.createSpace( container ); + this.createButton( container, "Exportieren", this.onExportPNGAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.createExportGIF = function() +{ + var container = this.createContainer( "Export GIF" ); + + this.createInputInteger( container, "Breite (Pixel):", 800, 0, 4096 ); + this.createInputInteger( container, "Höhe (Pixel):", 600, 0, 4096 ); + this.createSpace( container ); + this.createButton( container, "Exportieren", this.onExportGIFAccept.bind( this ) ); + + return container; +}; + +netgis.Modal.prototype.show = function( container ) +{ + this.root.classList.add( "netgis-show" ); + + // Active Container + var containers = this.root.getElementsByClassName( "netgis-dialog" ); + for ( var i = 0; i < containers.length; i++ ) + { + containers[ i ].classList.remove( "netgis-show" ); + } + + container.classList.add( "netgis-show" ); +}; + +netgis.Modal.prototype.hide = function() +{ + this.root.classList.remove( "netgis-show" ); +}; + +netgis.Modal.prototype.createContainer = function( title ) +{ + var container = document.createElement( "section" ); + container.className = "netgis-dialog netgis-shadow"; + + /*var header = document.createElement( "header" ); + header.className = "netgis-primary"; + */ + + var header = document.createElement( "header" ); + + var headerButton = document.createElement( "button" ); + headerButton.setAttribute( "type", "button" ); + headerButton.className = "netgis-primary netgis-hover-primary"; + headerButton.innerHTML = "

" + title + "

"; + headerButton.addEventListener( "click", this.onHeaderClick.bind( this ) ); + header.appendChild( headerButton ); + + var headerIcon = document.createElement( "span" ); + headerIcon.innerHTML = ""; + headerButton.appendChild( headerIcon ); + + container.appendChild( header ); + + var content = document.createElement( "div" ); + content.className = "netgis-modal-content"; + container.appendChild( content ); + + var table = document.createElement( "table" ); + content.appendChild( table ); + + return container; +}; + +netgis.Modal.prototype.createSpace = function( container ) +{ + var row = document.createElement( "tr" ); + row.className = "netgis-space"; + + var cell = document.createElement( "td" ); + cell.setAttribute( "colspan", 100 ); + row.appendChild( cell ); + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return cell; +}; + +netgis.Modal.prototype.createButton = function( container, title, callback ) +{ + var row = document.createElement( "tr" ); + + var cell = document.createElement( "td" ); + cell.setAttribute( "colspan", 100 ); + row.appendChild( cell ); + + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.className = "netgis-primary netgis-hover-primary"; + button.innerHTML = title; + if ( callback ) button.addEventListener( "click", callback ); + cell.appendChild( button ); + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return button; +}; + +netgis.Modal.prototype.createInputText = function( container, title ) +{ + var row = document.createElement( "tr" ); + //row.className = "netgis-hover-light"; + + var head = document.createElement( "th" ); + head.className = "netgis-padding"; + + var label = document.createElement( "label" ); + label.innerHTML = title; + head.appendChild( label ); + row.appendChild( head ); + + var cell = document.createElement( "td" ); + cell.className = "netgis-padding"; + + var input = document.createElement( "input" ); + input.setAttribute( "type", "text" ); + cell.appendChild( input ); + row.appendChild( cell ); + + label.htmlFor = input; + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return input; +}; + +netgis.Modal.prototype.createInputInteger = function( container, title, value, min, max ) +{ + var row = document.createElement( "tr" ); + //row.className = "netgis-hover-light"; + + var head = document.createElement( "th" ); + head.className = "netgis-padding"; + + var label = document.createElement( "label" ); + label.innerHTML = title; + head.appendChild( label ); + row.appendChild( head ); + + var cell = document.createElement( "td" ); + cell.className = "netgis-padding"; + + var input = document.createElement( "input" ); + input.setAttribute( "type", "number" ); + input.setAttribute( "min", min ); + input.setAttribute( "max", max ); + input.value = Number.parseInt( value ); + cell.appendChild( input ); + row.appendChild( cell ); + + label.htmlFor = input; + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return input; +}; + +netgis.Modal.prototype.createInputCheckbox = function( container, title, checked ) +{ + var row = document.createElement( "tr" ); + //row.className = "netgis-hover-light"; + + var head = document.createElement( "th" ); + head.className = "netgis-padding"; + + var label = document.createElement( "label" ); + label.innerHTML = title; + head.appendChild( label ); + row.appendChild( head ); + + var cell = document.createElement( "td" ); + cell.className = "netgis-padding"; + + var input = document.createElement( "input" ); + input.setAttribute( "type", "checkbox" ); + input.checked = checked; + cell.appendChild( input ); + row.appendChild( cell ); + + label.htmlFor = input; + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return input; +}; + +netgis.Modal.prototype.createInputFile = function( container, title, filetypes, callback ) +{ + var row = document.createElement( "tr" ); + //row.className = "netgis-hover-light"; + + var head = document.createElement( "th" ); + head.className = "netgis-padding"; + + var label = document.createElement( "label" ); + label.innerHTML = title; + head.appendChild( label ); + row.appendChild( head ); + + var cell = document.createElement( "td" ); + cell.className = "netgis-padding"; + + var input = document.createElement( "input" ); + input.setAttribute( "type", "file" ); + input.setAttribute( "accept", filetypes ); + if ( callback ) input.addEventListener( "change", callback ); + cell.appendChild( input ); + row.appendChild( cell ); + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return input; +}; + +netgis.Modal.prototype.createText = function( container, title, text ) +{ + var row = document.createElement( "tr" ); + //row.className = "netgis-padding"; + + var head = document.createElement( "th" ); + head.className = "netgis-padding"; + head.innerHTML = title; + row.appendChild( head ); + + var cell = document.createElement( "td" ); + cell.className = "netgis-padding"; + cell.innerHTML = text; + row.appendChild( cell ); + + var content = container.getElementsByClassName( "netgis-modal-content" )[ 0 ]; + var table = content.getElementsByTagName( "table" )[ 0 ]; + table.appendChild( row ); + + return cell; +}; + +netgis.Modal.prototype.onRootClick = function( e ) +{ + if ( e.target !== this.root ) return; + + this.hide(); +}; + +netgis.Modal.prototype.onHeaderClick = function( e ) +{ + this.hide(); +}; + +netgis.Modal.prototype.onImportGeoJSONShow = function( e ) +{ + var input = this.importGeoJSON.getElementsByTagName( "input" )[ 0 ]; + var button = this.importGeoJSON.getElementsByTagName( "button" )[ 1 ]; + input.value = ""; + button.disabled = true; + + this.show( this.importGeoJSON ); +}; + +netgis.Modal.prototype.onImportGeoJSONChange = function( e ) +{ + var input = this.importGeoJSON.getElementsByTagName( "input" )[ 0 ]; + var button = this.importGeoJSON.getElementsByTagName( "button" )[ 1 ]; + + if ( input.value && input.value.length > 0 ) + { + button.disabled = false; + } + else + { + button.disabled = true; + } +}; + +netgis.Modal.prototype.onImportGeoJSONAccept = function( e ) +{ + var input = this.importGeoJSON.getElementsByTagName( "input" )[ 0 ]; + var file = input.files[ 0 ]; + this.client.invoke( netgis.Events.IMPORT_GEOJSON, file ); + + this.hide(); +}; + +netgis.Modal.prototype.onImportGMLShow = function( e ) +{ + var input = this.importGML.getElementsByTagName( "input" )[ 0 ]; + var button = this.importGML.getElementsByTagName( "button" )[ 1 ]; + input.value = ""; + button.disabled = true; + + this.show( this.importGML ); +}; + +netgis.Modal.prototype.onImportGMLChange = function( e ) +{ + var input = this.importGML.getElementsByTagName( "input" )[ 0 ]; + var button = this.importGML.getElementsByTagName( "button" )[ 1 ]; + + if ( input.value && input.value.length > 0 ) + { + button.disabled = false; + } + else + { + button.disabled = true; + } +}; + +netgis.Modal.prototype.onImportGMLAccept = function( e ) +{ + var input = this.importGML.getElementsByTagName( "input" )[ 0 ]; + var file = input.files[ 0 ]; + this.client.invoke( netgis.Events.IMPORT_GML, file ); + + this.hide(); +}; + +netgis.Modal.prototype.onImportShapefileShow = function( e ) +{ + var input = this.importShapefile.getElementsByTagName( "input" )[ 0 ]; + var button = this.importShapefile.getElementsByTagName( "button" )[ 1 ]; + input.value = ""; + button.disabled = true; + + this.show( this.importShapefile ); +}; + +netgis.Modal.prototype.onImportShapefileChange = function( e ) +{ + var input = this.importShapefile.getElementsByTagName( "input" )[ 0 ]; + var button = this.importShapefile.getElementsByTagName( "button" )[ 1 ]; + + if ( input.value && input.value.length > 0 ) + { + button.disabled = false; + } + else + { + button.disabled = true; + } +}; + +netgis.Modal.prototype.onImportShapefileAccept = function( e ) +{ + var input = this.importShapefile.getElementsByTagName( "input" )[ 0 ]; + var file = input.files[ 0 ]; + this.client.invoke( netgis.Events.IMPORT_SHAPEFILE, file ); + + this.hide(); +}; + +netgis.Modal.prototype.onExportPDFShow = function( e ) +{ + var inputs = this.exportPDF.getElementsByTagName( "input" ); + inputs[ 0 ].value = this.client.map.getWidth(); + inputs[ 1 ].value = this.client.map.getHeight(); + inputs[ 2 ].value = this.client.config.export.defaultMargin; + inputs[ 3 ].checked = true; + + //TODO: refactor into single export modal with format select? + + this.show( this.exportPDF ); +}; + +netgis.Modal.prototype.onExportJPEGShow = function( e ) +{ + var inputs = this.exportJPEG.getElementsByTagName( "input" ); + inputs[ 0 ].value = this.client.map.getWidth(); + inputs[ 1 ].value = this.client.map.getHeight(); + + //TODO: refactor into single export modal with format select? + + this.show( this.exportJPEG ); +}; + +netgis.Modal.prototype.onExportPNGShow = function( e ) +{ + var inputs = this.exportPNG.getElementsByTagName( "input" ); + inputs[ 0 ].value = this.client.map.getWidth(); + inputs[ 1 ].value = this.client.map.getHeight(); + + //TODO: refactor into single export modal with format select? + + this.show( this.exportPNG ); +}; + +netgis.Modal.prototype.onExportGIFShow = function( e ) +{ + var inputs = this.exportGIF.getElementsByTagName( "input" ); + inputs[ 0 ].value = this.client.map.getWidth(); + inputs[ 1 ].value = this.client.map.getHeight(); + + //TODO: refactor into single export modal with format select? + + this.show( this.exportGIF ); +}; + +netgis.Modal.prototype.onExportPDFAccept = function( e ) +{ + var inputs = this.exportPDF.getElementsByTagName( "input" ); + var resx = Number.parseInt( inputs[ 0 ].value ); + var resy = Number.parseInt( inputs[ 1 ].value ); + var margin = Number.parseInt( inputs[ 2 ].value ); + var mode = inputs[ 3 ].checked; + this.client.invoke( netgis.Events.EXPORT_PDF, { resx: resx, resy: resy, mode: mode, margin: margin } ); + + this.hide(); +}; + +netgis.Modal.prototype.onExportJPEGAccept = function( e ) +{ + var inputs = this.exportJPEG.getElementsByTagName( "input" ); + var resx = Number.parseInt( inputs[ 0 ].value ); + var resy = Number.parseInt( inputs[ 1 ].value ); + this.client.invoke( netgis.Events.EXPORT_JPEG, { resx: resx, resy: resy } ); + + this.hide(); +}; + +netgis.Modal.prototype.onExportPNGAccept = function( e ) +{ + var inputs = this.exportPNG.getElementsByTagName( "input" ); + var resx = Number.parseInt( inputs[ 0 ].value ); + var resy = Number.parseInt( inputs[ 1 ].value ); + this.client.invoke( netgis.Events.EXPORT_PNG, { resx: resx, resy: resy } ); + + this.hide(); +}; + +netgis.Modal.prototype.onExportGIFAccept = function( e ) +{ + var inputs = this.exportGIF.getElementsByTagName( "input" ); + var resx = Number.parseInt( inputs[ 0 ].value ); + var resy = Number.parseInt( inputs[ 1 ].value ); + this.client.invoke( netgis.Events.EXPORT_GIF, { resx: resx, resy: resy } ); + + this.hide(); +}; \ No newline at end of file diff --git a/src/netgis/Modes.js b/src/netgis/Modes.js new file mode 100644 index 0000000..13bae62 --- /dev/null +++ b/src/netgis/Modes.js @@ -0,0 +1,26 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.Modes = Object.freeze +( + { + VIEW: "VIEW", + PANNING: "PANNING", + ZOOMING_IN: "ZOOMING_IN", + ZOOMING_OUT: "ZOOMING_OUT", + + DRAW_POINTS: "DRAW_POINTS", + DRAW_LINES: "DRAW_LINES", + DRAW_POLYGONS: "DRAW_POLYGONS", + + CUT_FEATURE_BEGIN: "CUT_FEATURE_BEGIN", + CUT_FEATURE_DRAW: "CUT_FEATURE_DRAW", + MODIFY_FEATURES: "MODIFY_FEATURES", + DELETE_FEATURES: "DELETE_FEATURES", + BUFFER_FEATURE_BEGIN: "BUFFER_FEATURE_BEGIN", + BUFFER_FEATURE_EDIT: "BUFFER_FEATURE_EDIT", + + SEARCH_PLACE: "SEARCH_PLACE" + } +); \ No newline at end of file diff --git a/src/netgis/OWS.js b/src/netgis/OWS.js new file mode 100644 index 0000000..d41a0e1 --- /dev/null +++ b/src/netgis/OWS.js @@ -0,0 +1,237 @@ +var netgis = netgis || {}; + +netgis.OWS = +( + function () + { + "use strict"; + + // Variables + + // Methods + var read = function( json, client ) + { + var config = + { + layers: [], + folders: [] + }; + + // Properties + if ( netgis.util.isDefined( json.properties ) ) + { + // BBox + var bbox = json.properties.bbox; + + /*if ( netgis.util.isDefined( bbox ) ) + { + client.invoke( netgis.Events.MAP_SET_EXTENT, { minx: bbox[ 0 ], miny: bbox[ 1 ], maxx: bbox[ 2 ], maxy: bbox[ 3 ] } ); + }*/ + + config.bbox = bbox; + } + + // Folders + var features = json.features; + + for ( var i = 0; i < features.length; i++ ) + { + var feature = features[ i ]; + + if ( feature.type === "Feature" ) + { + // Feature Properties + var props = feature.properties; + var path = props.folder; + + // Check Existing + var found = false; + + for ( var f = 0; f < config.folders.length; f++ ) + { + if ( config.folders[ f ].id === path ) + { + found = true; + break; + } + } + + if ( found ) continue; + + // Path Parts + var partsRaw = path.split( "/" ); + var parts = []; + + for ( var p = 0; p < partsRaw.length; p++ ) + { + var part = partsRaw[ p ]; + if ( part.length > 0 ) parts.push( part ); + } + + // Find Parent + var parent = -1; + + for ( var p = 0; p < parts.length; p++ ) + { + var part = parts[ p ]; + var partpath = "/" + parts.slice( 0, p + 1 ).join( "/" ); + + // Existing Folder + var exists = false; + + for ( var f = 0; f < config.folders.length; f++ ) + { + if ( config.folders[ f ].path === partpath ) + { + parent = f; + exists = true; + break; + } + } + + if ( exists ) continue; + + // Create New Folder + var index = config.folders.length; + + config.folders.push + ( + { + title: part, + parent: parent, + path: partpath + } + ); + + parent = index; + } + } + } + + // Features / Layers + for ( var i = 0; i < features.length; i++ ) + { + var feature = features[ i ]; + + if ( feature.type === "Feature" ) + { + //TODO: refactor to read feature function + + // Feature Properties + var props = feature.properties; + + // Folder + var folderIndex = -1; + + for ( var f = 0; f < config.folders.length; f++ ) + { + if ( config.folders[ f ].path === props.folder ) + { + folderIndex = f; + break; + } + } + + // Offerings + var offers = props.offerings; + + for ( var o = 0; o < offers.length; o++ ) + { + var offer = offers[ o ]; + + // Operationos + var ops = offer.operations; + + // Types + switch ( offer.code ) + { + // WMS + case "http://www.opengis.net/spec/owc-geojson/1.0/req/wms": + { + var getCaps = ops[ 0 ]; + var url = getCaps.href; + + config.layers.push + ( + { + folder: folderIndex, + type: netgis.LayerTypes.WMS, + url: url, + title: props.title, + attribution: props.rights, + active: props.active + } + ); + + break; + } + + // XYZ + case "http://www.opengis.net/spec/owc-geojson/1.0/req/xyz": + { + var getTile = ops[ 0 ]; + + config.layers.push + ( + { + folder: folderIndex, + type: netgis.LayerTypes.XYZ, + url: getTile.href, + title: props.title, + attribution: props.rights, + active: props.active + } + ); + + break; + } + + // OSM / XYZ + case "http://www.opengis.net/spec/owc-geojson/1.0/req/osm": + { + // Operations + /*for ( var oi = 0; oi < ops.length; oi++ ) + { + var op = ops[ oi ]; + + switch ( op.code ) + { + case "" + } + }*/ + + var getTile = ops[ 0 ]; + + config.layers.push + ( + { + folder: folderIndex, + type: netgis.LayerTypes.XYZ, + url: getTile.href, + title: props.title, + attribution: props.rights, + active: props.active + } + ); + + break; + } + } + } + + } + + } // end for each feature + + client.invoke( netgis.Events.CONTEXT_UPDATE, config ); + }; + + // Public Interface + var iface = + { + read: read + }; + + return iface; + } +)(); \ No newline at end of file diff --git a/src/netgis/SLD.js b/src/netgis/SLD.js new file mode 100644 index 0000000..b12db13 --- /dev/null +++ b/src/netgis/SLD.js @@ -0,0 +1,79 @@ +var netgis = netgis || {}; + +//NOTE: https://portal.ogc.org/files/?artifact_id=22364 p. 53 + +netgis.SLD = +( + function () + { + "use strict"; + + // Variables + + // Methods + var read = function( data, client ) + { + var parser = new DOMParser(); + var xml = parser.parseFromString( data, "text/xml" ); + + var style = {}; + + // Layers + var layers = xml.getElementsByTagName( "NamedLayer" ); + + for ( var i = 0; i < layers.length; i++ ) + { + var layer = layers[ i ]; + var name = layer.getElementsByTagName( "se:Name" )[ 0 ].innerHTML; + + console.info( "Layer:", name ); + + // Type Styles + var typestyles = layer.getElementsByTagName( "se:FeatureTypeStyle" ); + + for ( var j = 0; j < typestyles.length; j++ ) + { + var typestyle = typestyles[ j ]; + + // Rules + var rules = typestyle.getElementsByTagName( "se:Rule" ); + + for ( var k = 0; k < rules.length; k++ ) + { + var rule = rules[ k ]; + var rulename = rule.getElementsByTagName( "se:Name" )[ 0 ].innerHTML; + + console.info( "Rule:", rulename ); + + // Polygon Symbolizer + var polysymbol = rule.getElementsByTagName( "se:PolygonSymbolizer" )[ 0 ]; + var polyfill = polysymbol.getElementsByTagName( "se:Fill" )[ 0 ]; + var polystroke = polysymbol.getElementsByTagName( "se:Stroke" )[ 0 ]; + + style[ "polygon" ] = + { + //fill: polyfill.getElementsByTagName( "se:SvgParameter" ) + fill: polyfill.querySelector( "[name='fill']" ).innerHTML, + stroke: polystroke.querySelector( "[name='stroke']" ).innerHTML, + strokeWidth: Number.parseFloat( polystroke.querySelector( "[name='stroke-width']" ).innerHTML ) + }; + } + } + } + + console.info( "SLD:", style ); + + client.invoke( netgis.Events.MAP_UPDATE_STYLE, style ); + + return style; + }; + + // Public Interface + var iface = + { + read: read + }; + + return iface; + } +)(); \ No newline at end of file diff --git a/src/netgis/SearchPlace.js b/src/netgis/SearchPlace.js new file mode 100644 index 0000000..b677507 --- /dev/null +++ b/src/netgis/SearchPlace.js @@ -0,0 +1,59 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.SearchPlace = function() +{ + this.client = null; + this.timeout = null; + this.lastRequest = null; +}; + +netgis.SearchPlace.prototype.load = function() +{ + this.client.on( netgis.Events.SEARCH_PLACE_REQUEST, this.onSearchPlaceRequest.bind( this ) ); +}; + +netgis.SearchPlace.prototype.request = function( query ) +{ + //NOTE: https://www.geoportal.rlp.de/mapbender/geoportal/gaz_geom_mobile.php?outputFormat=json&resultTarget=web&searchEPSG=25832&maxResults=5&maxRows=5&searchText=trier&featureClass=P&style=full&name_startsWith=trier + + //TODO: get url with query template from config + ////var url = "https://www.geoportal.rlp.de/mapbender/geoportal/gaz_geom_mobile.php?outputFormat=json&resultTarget=web&searchEPSG={epsg}&maxResults=5&maxRows=5&featureClass=P&style=full&searchText={q}&name_startsWith={q}"; + ////url = "./proxy.php?" + url; + + if ( this.client.config.search && this.client.config.search.url ) + { + var url = this.client.config.search.url; + + var q = query; + q = q.trim(); + + url = netgis.util.replace( url, "{q}", window.encodeURIComponent( q ) ); + url = netgis.util.replace( url, "{epsg}", 4326 ); // 25823 + url = window.encodeURI( url ); + + this.lastRequest = netgis.util.request( url, this.onSearchPlaceResponse.bind( this ) ); + } + else + { + console.warn( "No search API url configured for place search!" ); + } +}; + +netgis.SearchPlace.prototype.onSearchPlaceRequest = function( e ) +{ + var query = e.query; + var self = this; + + // Debounce Request + if ( this.lastRequest ) this.lastRequest.abort(); + if ( this.timeout ) window.clearTimeout( this.timeout ); + this.timeout = window.setTimeout( function() { self.request( query ); }, 300 ); +}; + +netgis.SearchPlace.prototype.onSearchPlaceResponse = function( data ) +{ + var json = JSON.parse( data ); + this.client.invoke( netgis.Events.SEARCH_PLACE_RESPONSE, json ); +}; \ No newline at end of file diff --git a/src/netgis/Theme.css b/src/netgis/Theme.css new file mode 100644 index 0000000..bf97fd4 --- /dev/null +++ b/src/netgis/Theme.css @@ -0,0 +1,68 @@ +/* TODO: rename to config.css ? */ + +/* Font */ + +.netgis-client, .netgis-client button +{ + font-family: Verdana, sans-serif; + font-size: 4mm; +} + +/* Colors */ + +/* TODO: !important on colors necessary ? */ + +.netgis-primary { background-color: #a7233f !important; color: white !important; } +.netgis-hover-primary:hover { background-color: #c82a4b !important; color: white !important; } +.netgis-hover-light:hover { background-color: #f4f4f4 !important; } +.netgis-text-primary { color: #a7233f !important; } +.netgis-hover-text-primary:hover { color: #c82a4b !important; } +.netgis-dialog { background: #fff; color: #000; } +.netgis-dropdown:hover > button { background-color: #c82a4b !important; } + +/* Effects */ + +.netgis-shadow +{ + box-shadow: 0mm 0.5mm 1mm 0mm rgba( 0, 0, 0, 0.2 ), 0mm 1mm 2.5mm 0mm rgba( 0, 0, 0, 0.1 ) !important; +} + +.netgis-shadow-large +{ + box-shadow: 0mm 1mm 2mm 0mm rgba( 0, 0, 0, 0.3 ), 0mm 2mm 5mm 0mm rgba( 0, 0, 0, 0.15 ) !important; +} + +.netgis-text-shadow +{ + text-shadow: 0mm 0mm 1mm rgba( 0, 0, 0, 1.0 ); +} + +/* Elements */ +/* +.netgis-client button +{ + min-height: 12mm; + min-width: 12mm; + /*padding: 0mm;* + border: none; + background: none; + /*color: white;* + cursor: pointer; +} +*/ + +.netgis-client button +{ + border: none; + background: none; + cursor: pointer; +} + +/* Format */ + +.netgis-clip-text +{ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/src/netgis/Toolbar.css b/src/netgis/Toolbar.css new file mode 100644 index 0000000..84ce61b --- /dev/null +++ b/src/netgis/Toolbar.css @@ -0,0 +1,145 @@ +.netgis-toolbars +{ + position: absolute; + left: 0mm; + right: 0mm; + top: 0mm; +} + +.netgis-toolbar +{ + position: absolute; + left: 0mm; + right: 0mm; + /*min-width: 100%; /* NOTE: https://stackoverflow.com/questions/10891293/prevent-float-left-div-from-going-to-a-new-line */ + top: 12mm; + min-height: 12mm; + line-height: 12mm; + font-size: 0mm; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + z-index: 1; + + -webkit-transform: none; + transform: none; + transition: transform 150ms ease; +} + +.netgis-toolbar.netgis-hide +{ + -webkit-transform: translateY( -24mm ); + transform: translateY( -24mm ); + transition: transform 150ms ease; + will-change: transform; +} + +.netgis-toolbar > div +{ + height: 12mm; + white-space: nowrap; +} + +.netgis-toolbar > div > * +{ + display: inline-block; + font-size: 4mm; + /*float: left; /* TODO: float cause line breaks */ +} + +.netgis-toolbar button +{ + /*padding: 0mm 4mm;*/ + padding: 0mm 4mm 0mm 0mm; +} + +.netgis-toolbar > div > button +{ + line-height: 12mm; +} + +.netgis-toolbar button i +{ + /*margin-right: 4mm;*/ + width: 12mm; +} + +.netgis-toolbar button:last-child +{ + padding-right: 0mm; +} + +.netgis-toolbar button:last-child span +{ + margin-right: 4mm; +} + +/*.netgis-toolbar button i:last-child +{ + margin-right: 0mm; +}*/ + +.netgis-toolbar label +{ + display: inline-block; + height: 12mm; + padding: 0mm 4mm; + cursor: pointer; +} + +.netgis-toolbar input[type=checkbox] +{ + margin-right: 2mm; +} + +.netgis-toolbar input[type=number] +{ + margin-left: 2mm; + width: 20mm; +} + +.netgis-toolbar input[type=text] +{ + /*margin-left: 2mm;*/ + width: 60mm; +} + +/* TODO: refactor to abstract common dropdown list style ( see head menu ) */ + +.netgis-toolbar .netgis-search-list +{ + position: fixed; + min-width: 68mm; /* 60mm + 4mm + 4mm ( input width + padding ) */ + /*osition: absolute; + left: 0mm; + min-width: 100%;*/ + /*height: 5.0em;*/ + padding: 0mm; + margin: 0mm; + margin-left: -4mm; + z-index: 1; + list-style-type: none; + overflow: hidden; + box-shadow: 0mm 0.5mm 0.5mm 0mm rgba( 0, 0, 0, 0.2 )/*, 0mm 1mm 2.5mm 0mm rgba( 0, 0, 0, 0.1 )*/ !important; +} + +.netgis-toolbar .netgis-search-list.netgis-hide +{ + display: none; +} + +.netgis-toolbar .netgis-search-list li +{ + width: 100%; + padding: 0mm; + margin: 0mm; +} + +.netgis-toolbar .netgis-search-list li button +{ + width: 100%; + height: 12mm; + padding: 0mm 4mm 0mm 4mm; + white-space: nowrap; + text-align: left; +} \ No newline at end of file diff --git a/src/netgis/Toolbar.js b/src/netgis/Toolbar.js new file mode 100644 index 0000000..875fc54 --- /dev/null +++ b/src/netgis/Toolbar.js @@ -0,0 +1,447 @@ +"use strict"; + +var netgis = netgis || {}; + +netgis.Toolbar = function() +{ + this.client = null; + this.toolbars = {}; + this.searchValue = ""; +}; + +netgis.Toolbar.prototype.load = function() +{ + var config = this.client.config; + + this.root = document.createElement( "section" ); // header? + this.root.className = "netgis-toolbars"; + + if ( this.client.editable ) + { + // Draw + this.toolbars[ netgis.Modes.DRAW_POINTS ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.DRAW_POINTS ], this.createToolbarButton( 'Punkte zeichnen:', this.onToolbarClose.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.DRAW_POINTS ], this.createToolbarCheckbox( "Einrasten", this.onSnapChange.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.DRAW_POINTS ] ); + + this.toolbars[ netgis.Modes.DRAW_LINES ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.DRAW_LINES ], this.createToolbarButton( 'Linien zeichnen:', this.onToolbarClose.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.DRAW_LINES ], this.createToolbarCheckbox( "Einrasten", this.onSnapChange.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.DRAW_LINES ] ); + + this.toolbars[ netgis.Modes.DRAW_POLYGONS ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.DRAW_POLYGONS ], this.createToolbarButton( 'Polygone zeichnen:', this.onToolbarClose.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.DRAW_POLYGONS ], this.createToolbarCheckbox( "Einrasten", this.onSnapChange.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.DRAW_POLYGONS ] ); + + // Edit + this.toolbars[ netgis.Modes.CUT_FEATURE_BEGIN ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.CUT_FEATURE_BEGIN ], this.createToolbarButton( 'Feature zum Ausschneiden wählen:', this.onToolbarClose.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.CUT_FEATURE_BEGIN ] ); + + this.toolbars[ netgis.Modes.CUT_FEATURE_DRAW ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.CUT_FEATURE_DRAW ], this.createToolbarButton( 'Fläche zum Ausschneiden zeichnen:', this.onToolbarClose.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.CUT_FEATURE_DRAW ] ); + + this.toolbars[ netgis.Modes.MODIFY_FEATURES ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.MODIFY_FEATURES ], this.createToolbarButton( 'Features verschieben:', this.onToolbarClose.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.MODIFY_FEATURES ] ); + + this.toolbars[ netgis.Modes.DELETE_FEATURES ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.DELETE_FEATURES ], this.createToolbarButton( 'Features löschen:', this.onToolbarClose.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.DELETE_FEATURES ] ); + + this.toolbars[ netgis.Modes.BUFFER_FEATURE_BEGIN ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.BUFFER_FEATURE_BEGIN ], this.createToolbarButton( 'Feature zum Puffern wählen:', this.onToolbarClose.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.BUFFER_FEATURE_BEGIN ] ); + + this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ] = this.createToolbar(); + + //var wrapper = document.createElement( "div" ); + //this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].appendChild( wrapper ); + + var bufferDefaultRadius = 1000; + var bufferDefaultSegments = 3; + + if ( netgis.util.isDefined( config.tools ) ) + { + if ( netgis.util.isDefined( config.tools.buffer.defaultRadius ) ) bufferDefaultRadius = config.tools.buffer.defaultRadius; + if ( netgis.util.isDefined( config.tools.buffer.defaultSegments ) ) bufferDefaultSegments = config.tools.buffer.defaultSegments; + } + + this.append( this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ], this.createToolbarButton( 'Feature puffern:', this.onBufferCancel.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ], this.createToolbarInput( "Radius in Meter:", bufferDefaultRadius, this.onBufferChange.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ], this.createToolbarInput( "Segmente:", bufferDefaultSegments, this.onBufferChange.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ], this.createToolbarButton( 'OK', this.onBufferAccept.bind( this ) ) ); + + //var self = this; + //setTimeout( function() { self.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].classList.remove( "netgis-hide" ); }, 100 ); + + /* + this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].appendChild( this.createToolbarButton( 'Feature puffern:', this.onBufferCancel.bind( this ) ) ); + this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].appendChild( this.createToolbarInput( "Radius in Meter:", this.client.config.tools.buffer.defaultRadius, this.onBufferChange.bind( this ) ) ); + this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].appendChild( this.createToolbarInput( "Segmente:", this.client.config.tools.buffer.defaultSegments, this.onBufferChange.bind( this ) ) ); + */ + + var bufferInputs = this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].getElementsByTagName( "input" ); + bufferInputs[ 0 ].addEventListener( "keyup", this.onBufferKeyUp.bind( this ) ); + bufferInputs[ 1 ].addEventListener( "keyup", this.onBufferKeyUp.bind( this ) ); + bufferInputs[ 1 ].setAttribute( "min", 1 ); + + //this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].appendChild( this.createToolbarButton( 'OK', this.onBufferAccept.bind( this ) ) ); + this.root.appendChild( this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ] ); + } + + // Search + this.toolbars[ netgis.Modes.SEARCH_PLACE ] = this.createToolbar(); + this.append( this.toolbars[ netgis.Modes.SEARCH_PLACE ], this.createToolbarButton( 'Suche:', this.onToolbarClose.bind( this ) ) ); + + var searchInput = this.createToolbarInputText( "Adresse...", "", null ); + searchInput.style.position = "relative"; + + this.searchInput = searchInput.getElementsByTagName( "input" )[ 0 ]; + //this.searchInput.addEventListener( "change", this.onSearchChange.bind( this ) ); + //this.searchInput.addEventListener( "keypress", this.onSearchKeyPress.bind( this ) ); + this.searchInput.addEventListener( "keyup", this.onSearchKeyUp.bind( this ) ); + this.searchInput.addEventListener( "focus", this.onSearchFocus.bind( this ) ); + this.searchInput.addEventListener( "blur", this.onSearchBlur.bind( this ) ); + + /*var searchClear = document.createElement( "button" ); + searchClear.innerHTML = ""; + searchInput.appendChild( searchClear );*/ + + this.append( this.toolbars[ netgis.Modes.SEARCH_PLACE ], searchInput ); + + this.searchList = document.createElement( "ul" ); + this.searchList.className = "netgis-dropdown-content netgis-search-list netgis-dialog netgis-shadow netgis-hide"; + searchInput.appendChild( this.searchList ); + + //TODO: search dropdown not working if toolbar has overflow-y hidden + + //TODO: refactor search list to abstract dropdown component ( see head menus ) + + //this.toolbars[ netgis.MapModes.SEARCH_PLACE ].appendChild( this.createToolbarButton( 'OK', this.onBufferAccept.bind( this ) ) ); + this.append( this.toolbars[ netgis.Modes.SEARCH_PLACE ], this.createToolbarButton( '', this.onSearchClear.bind( this ) ) ); + + this.root.appendChild( this.toolbars[ netgis.Modes.SEARCH_PLACE ] ); + + this.client.root.appendChild( this.root ); + + // Events + this.client.on( netgis.Events.SET_MODE, this.onSetMode.bind( this ) ); + this.client.on( netgis.Events.SEARCH_PLACE_RESPONSE, this.onSearchPlaceResponse.bind( this ) ); +}; + +netgis.Toolbar.prototype.createToolbar = function() +{ + var toolbar = document.createElement( "div" ); + toolbar.className = "netgis-toolbar netgis-dialog netgis-shadow netgis-hide"; + + var wrapper = document.createElement( "div" ); + toolbar.appendChild( wrapper ); + + return toolbar; +}; + +netgis.Toolbar.prototype.append = function( toolbar, child ) +{ + var wrapper = toolbar.getElementsByTagName( "div" )[ 0 ]; + wrapper.appendChild( child ); +}; + +netgis.Toolbar.prototype.createToolbarButton = function( content, callback ) +{ + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.className = "netgis-hover-light"; + button.innerHTML = content; + button.addEventListener( "click", callback ); + + return button; +}; + +netgis.Toolbar.prototype.createToolbarCheckbox = function( title, callback ) +{ + var label = document.createElement( "label" ); + label.className = "netgis-hover-light"; + + var checkbox = document.createElement( "input" ); + checkbox.setAttribute( "type", "checkbox" ); + checkbox.addEventListener( "change", callback ); + label.appendChild( checkbox ); + + var text = document.createTextNode( title ); + label.appendChild( text ); + + return label; +}; + +netgis.Toolbar.prototype.createToolbarInput = function( title, value, callback ) +{ + var label = document.createElement( "label" ); + label.className = "netgis-hover-light"; + + var text = document.createTextNode( title ); + label.appendChild( text ); + + var input = document.createElement( "input" ); + input.setAttribute( "type", "number" ); + input.setAttribute( "min", 0 ); + input.value = value; + input.addEventListener( "change", callback ); + label.appendChild( input ); + + return label; +}; + +netgis.Toolbar.prototype.createToolbarInputText = function( title, value, callback ) +{ + var label = document.createElement( "label" ); + label.className = "netgis-hover-light"; + + /*var text = document.createTextNode( title ); + label.appendChild( text );*/ + + var input = document.createElement( "input" ); + input.setAttribute( "type", "text" ); + input.setAttribute( "placeholder", title ); + input.value = value; + if ( callback ) input.addEventListener( "change", callback ); + label.appendChild( input ); + + return label; +}; + +netgis.Toolbar.prototype.onSetMode = function( e ) +{ + var mode = e; + + // Store old search mode to allow toggling search toolbar + var searchMode = ! this.toolbars[ netgis.Modes.SEARCH_PLACE ].classList.contains( "netgis-hide" ); + + // Toggle Toolbars + netgis.util.foreach + ( + this.toolbars, + function( index, toolbar ) + { + if ( index === mode ) + toolbar.classList.remove( "netgis-hide" ); + else + toolbar.classList.add( "netgis-hide" ); + } + ); + + // Mode + switch ( mode ) + { + case netgis.Modes.SEARCH_PLACE: + { + //TODO: toggle mode event ? + + if ( ! searchMode ) + this.searchInput.focus(); + else + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.VIEW ); + + break; + } + + case netgis.Modes.BUFFER_FEATURE_EDIT: + { + this.updateBuffer(); + break; + } + } +}; + +netgis.Toolbar.prototype.onToolbarClose = function( e ) +{ + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.VIEW ); +}; + +netgis.Toolbar.prototype.searchRequest = function( value ) +{ + value = value.trim(); + + if ( value !== this.searchValue ) + { + this.searchValue = value; + + if ( value.length > 0 ) + { + this.client.invoke( netgis.Events.SEARCH_PLACE_REQUEST, { query: value } ); + } + } + + //TODO: refactor into search module ? +}; + +netgis.Toolbar.prototype.searchClear = function() +{ + this.searchInput.value = ""; + this.searchList.innerHTML = ""; +}; + +netgis.Toolbar.prototype.searchSelectFirst = function() +{ + var buttons = this.searchList.getElementsByTagName( "button" ); + + if ( buttons.length > 0 ) + { + this.onSearchItemClick( { target: buttons[ 0 ] } ); + } +}; + +netgis.Toolbar.prototype.onSearchKeyUp = function( e ) +{ + var input = e.target; + var key = e.keyCode; + + // a-z = 65-90 + // 0-9 = 48-57 + + switch ( key ) + { + // Enter + case 13: + { + this.searchSelectFirst(); + this.searchList.classList.add( "netgis-hide" ); + break; + } + + // Escape + case 27: + { + this.searchClear(); + break; + } + + default: + { + this.searchRequest( input.value ); + break; + } + } +}; + +netgis.Toolbar.prototype.onSearchChange = function( e ) +{ + var input = e.target; + this.searchRequest( input.value ); + + /*this.client.invoke( netgis.Events.SEARCH_PLACE_REQUEST, { query: input.value } ); + console.info( "Search Change:", input.value );*/ +}; + +netgis.Toolbar.prototype.onSearchClear = function( e ) +{ + this.searchClear(); + this.searchInput.focus(); +}; + +netgis.Toolbar.prototype.onSearchFocus = function( e ) +{ + this.searchList.classList.remove( "netgis-hide" ); +}; + +netgis.Toolbar.prototype.onSearchBlur = function( e ) +{ + this.searchList.classList.add( "netgis-hide" ); +}; + +netgis.Toolbar.prototype.onSearchPlaceResponse = function( e ) +{ + this.searchList.innerHTML = ""; + + var results = e.geonames; + + for ( var i = 0; i < results.length; i++ ) + { + var result = results[ i ]; + + var item = document.createElement( "li" ); + item.className = "netgis-hover-light"; + + var button = document.createElement( "button" ); + button.setAttribute( "type", "button" ); + button.innerHTML = result.title; + button.dataset.title = result.title; + button.dataset.minx = result.minx; + button.dataset.miny = result.miny; + button.dataset.maxx = result.maxx; + button.dataset.maxy = result.maxy; + button.addEventListener( "mousedown", this.onSearchItemClick.bind( this ) ); + item.appendChild( button ); + + this.searchList.appendChild( item ); + } +}; + +netgis.Toolbar.prototype.onSearchItemClick = function( e ) +{ + var button = e.target; + + var title = button.dataset.title; + var minx = Number.parseFloat( button.dataset.minx ); + var miny = Number.parseFloat( button.dataset.miny ); + var maxx = Number.parseFloat( button.dataset.maxx ); + var maxy = Number.parseFloat( button.dataset.maxy ); + + this.client.invoke( netgis.Events.MAP_SET_EXTENT, { minx: minx, miny: miny, maxx: maxx, maxy: maxy } ); + + //this.searchInput.value = title; + this.searchValue = title; +}; + +netgis.Toolbar.prototype.updateBuffer = function() +{ + var inputs = this.toolbars[ netgis.Modes.BUFFER_FEATURE_EDIT ].getElementsByTagName( "input" ); + var radius = Number.parseFloat( inputs[ 0 ].value ); + var segments = Number.parseInt( inputs[ 1 ].value ); + + this.client.invoke( netgis.Events.BUFFER_CHANGE, { radius: radius, segments: segments } ); +}; + +netgis.Toolbar.prototype.onBufferChange = function( e ) +{ + this.updateBuffer(); +}; + +netgis.Toolbar.prototype.onBufferKeyUp = function( e ) +{ + var key = e.keyCode; + + // Enter + //if ( key === 13 ) + { + this.updateBuffer(); + } +}; + +netgis.Toolbar.prototype.onBufferAccept = function( e ) +{ + this.client.invoke( netgis.Events.BUFFER_ACCEPT, null ); + //this.setMode( netgis.Modes.VIEW ); + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.VIEW ); +}; + +netgis.Toolbar.prototype.onBufferCancel = function( e ) +{ + this.client.invoke( netgis.Events.BUFFER_CANCEL, null ); + //this.setMode( netgis.Modes.VIEW ); + this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.VIEW ); +}; + +netgis.Toolbar.prototype.onSnapChange = function( e ) +{ + var input = e.target; + var on = input.checked; + + this.toolbars[ netgis.Modes.DRAW_POINTS ].getElementsByTagName( "input" )[ 0 ].checked = on; + this.toolbars[ netgis.Modes.DRAW_LINES ].getElementsByTagName( "input" )[ 0 ].checked = on; + this.toolbars[ netgis.Modes.DRAW_POLYGONS ].getElementsByTagName( "input" )[ 0 ].checked = on; + + this.client.invoke( on ? netgis.Events.SNAP_ON : netgis.Events.SNAP_OFF, null ); +}; \ No newline at end of file diff --git a/src/netgis/Util.js b/src/netgis/Util.js new file mode 100644 index 0000000..ad7f8c9 --- /dev/null +++ b/src/netgis/Util.js @@ -0,0 +1,161 @@ +var netgis = netgis || {}; + +/** + * General purpose pure static utility functions. + */ +netgis.util = +( + function () + { + "use strict"; + + // Methods + var isDefined = function( v ) + { + return ( typeof v !== "undefined" ); + }; + + var isString = function( v ) + { + return ( typeof v === "string" || v instanceof String ); + }; + + /** + * Replace all string occurences. + * @param {String} str + * @param {String} find + * @param {String} newstr + * @returns {String} + */ + var replace = function( str, find, newstr ) + { + return str.replace( new RegExp( find, "g" ), newstr ); + }; + + var foreach = function( obj, fn ) + { + for ( var k in obj ) + { + if ( obj.hasOwnProperty( k ) ) + { + fn( k, obj[ k ] ); + } + } + }; + + /** + * Replace template strings in html string. + * @param {type} html String with "{key}" placeholders. + * @param {type} data Object of key value pairs to insert. + * @returns {String} The modified html string. + */ + var template = function( html, data ) + { + // Get Template: $( "#template-" + name ).text(); + + foreach + ( + data, + function( key, value ) + { + html = html.replace( new RegExp( "{" + key + "}", "g" ), value ); + } + ); + + return html; + }; + + /** + * Create HTML element from string. + * @param {String} html + * @returns {Element} + */ + var create = function( html ) + { + var temp = document.createElement( "tbody" ); + temp.innerHTML = html; + + return temp.children[ 0 ]; + }; + + /** + * Replace new line characters with HTML line breaks. + * @param {String} str + * @returns {String} + */ + var newlines = function( str ) + { + return str.replace( new RegExp( "\n", "g" ), "
" ); + }; + + /** + * Calculate the byte size of an object. + * @param {Object} json + * @returns {Object} Object with size info ( bytes, kilobytes, megabytes ) + */ + var size = function( json ) + { + var bytes = new TextEncoder().encode( JSON.stringify( json ) ).length; + var kilobytes = bytes / 1024; + var megabytes = kilobytes / 1024; + + return { bytes: bytes, kilobytes: kilobytes, megabytes: megabytes }; + }; + + /** + * Send async GET request. + * @param {String} url + * @param {function} callback + */ + var request = function( url, callback ) + { + var request = new XMLHttpRequest(); + request.onload = function() { callback( this.responseText ); }; + request.open( "GET", url, true ); + request.send(); + }; + + /** + * Pad string with leading zeros. + * @param {String} s The string to pad. + * @param {Integer} n Minimum number of digits. + * @returns {String} The padded string. + */ + var padstr = function( s, n ) + { + var o = s.toString(); + while ( o.length < n ) o = "0" + o; + + return o; + }; + + /** + * Merge object properties. + * @param {Object} target Merged object properties will be written to this. + * @param {Object} other The object to append to the target object. + * @returns {Object} The modified target object. + */ + var merge = function( target, other ) + { + return Object.assign( target, other ); + }; + + // Public Interface + var iface = + { + isDefined: isDefined, + isString: isString, + replace: replace, + foreach: foreach, + template: template, + newlines: newlines, + create: create, + size: size, + request: request, + padstr: padstr, + merge: merge + }; + + return iface; + } +)(); \ No newline at end of file