Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Progress toward simple asset editor.

  • Loading branch information...
commit 9c103bbebcc93023dfe28c8fe6eb0dbb98069d6e 1 parent 8f6a86f
@tschaub tschaub authored
View
207 app/static/assets.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>SFPark - Asset Management</title>
+
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <link rel="shortcut icon" href="theme/app/img/favicon.ico">
+
+ <!-- Ext resources -->
+ <link rel="stylesheet" type="text/css" href="externals/ext/resources/css/ext-all.css">
+ <link rel="stylesheet" type="text/css" href="externals/ext/resources/css/xtheme-gray.css">
+ <script type="text/javascript" src="externals/ext/adapter/ext/ext-base-debug.js"></script>
+ <script type="text/javascript" src="externals/ext/ext-all-debug.js"></script>
+
+ <!-- OpenLayers resources -->
+ <link rel="stylesheet" type="text/css" href="externals/openlayers/theme/default/style.css">
+ <script type="text/javascript" src="script/OpenLayers.js"></script>
+
+ <!-- GeoExt resources -->
+ <link rel="stylesheet" type="text/css" href="externals/geoext/resources/css/popup.css">
+ <link rel="stylesheet" type="text/css" href="externals/geoext/resources/css/layerlegend.css">
+ <link rel="stylesheet" type="text/css" href="externals/geoext/resources/css/gxtheme-gray.css">
+ <script type="text/javascript" src="script/GeoExt.js"></script>
+
+ <!-- ux resources -->
+ <script type="text/javascript" src="script/ux.js"></script>
+
+ <!-- gxp resources -->
+ <link rel="stylesheet" type="text/css" href="externals/gxp/src/theme/all.css">
+ <script type="text/javascript" src="script/gxp.js"></script>
+
+ <!-- app resources -->
+ <link rel="stylesheet" type="text/css" href="theme/app/style.css">
+ <script type="text/javascript" src="script/app/ParkingManager/AssetEditorPopup.js"></script>
+
+ <script type="text/javascript">
+
+ Ext.QuickTips.init();
+ Ext.BLANK_IMAGE_URL = "theme/app/img/blank.gif";
+ OpenLayers.DOTS_PER_INCH = 25.4 / 0.28;
+ OpenLayers.ImgPath = "externals/openlayers/img/";
+ OpenLayers.ProxyHost = "proxy/?url=";
+ OpenLayers.Layer.WMS.prototype.DEFAULT_PARAMS.transparent = true;
+
+ var app = new gxp.Viewer({
+
+ portalItems: [{
+ region: "center",
+ layout: "border",
+ tbar: {
+ id: "paneltbar",
+ items: [{
+ iconCls: "icon-geoexplorer",
+ disabled: true
+ }, "SFPark", "-", "-"]
+ },
+ items: [{
+ xtype: "panel",
+ region: "west",
+ width: 210,
+ split: true,
+ collapsible: true,
+ collapseMode: "mini",
+ id: "tree",
+ title: "Layers"
+ }, "map"]
+ }],
+
+ mapItems: [{
+ xtype: "gx_zoomslider",
+ vertical: true,
+ height: 100
+ }],
+
+ sources: {
+ local: {
+ ptype: "gxp_wmssource",
+ url: "/geoserver/ows",
+ title: "Local GeoServer",
+ version: "1.1.1"
+ },
+ osm: {
+ ptype: "gxp_osmsource"
+ },
+ google: {
+ ptype: "gxp_googlesource"
+ },
+ ol: {
+ ptype: "gxp_olsource"
+ }
+ },
+
+ map: {
+ id: "map",
+ region: "center",
+ projection: "EPSG:900913",
+ units: "m",
+ numZoomLevels: 21,
+ maxResolution: 156543.0339,
+ maxExtent: [
+ -20037508.34, -20037508.34,
+ 20037508.34, 20037508.34
+ ],
+ extent: [-13650159, 4534735, -13609227, 4554724],
+ layers: [{
+ source: "google",
+ title: "Road Map",
+ name: "ROADMAP",
+ group: "background",
+ visibility: false
+ }, {
+ source: "google",
+ title: "Hybrid Map",
+ name: "HYBRID",
+ group: "background",
+ visibility: false
+ }, {
+ source: "google",
+ title: "Satellite Map",
+ name: "SATELLITE",
+ group: "background",
+ visibility: false
+ }, {
+ source: "google",
+ title: "Terrain Map",
+ name: "TERRAIN",
+ group: "background",
+ visibility: false
+ }, {
+ source: "ol",
+ group: "background",
+ fixed: true,
+ type: "OpenLayers.Layer",
+ args: [
+ "None", {visibility: false}
+ ]
+ }, {
+ source: "local",
+ name: "sfpark:city_curbs",
+ visibility: false
+ }, {
+ source: "local",
+ name: "sfpark:spaces",
+ selected: true,
+ }],
+ center: [-13629218, 4545403],
+ zoom: 13
+ },
+
+
+ tools: [{
+ ptype: "gxp_zoomtoextent",
+ actionTarget: {target: "paneltbar", index: 3}
+ }, {
+ ptype: "gxp_navigationhistory",
+ actionTarget: {target: "paneltbar", index: 4}
+ }, {
+ ptype: "gxp_layertree",
+ outputConfig: {
+ id: "layertree",
+ tbar: []
+ },
+ outputTarget: "tree"
+ }, {
+ ptype: "gxp_addlayers",
+ actionTarget: "layertree.tbar"
+ }, {
+ ptype: "gxp_removelayer",
+ actionTarget: ["layertree.tbar", "layertree.contextMenu"]
+ }, {
+ ptype: "gxp_snappingagent",
+ id: "curb-snapping",
+ targets: [{
+ source: "local",
+ name: "sfpark:city_curbs",
+ maxResolution: 0.6,
+ node: false,
+ vertex: false,
+ tolerance: 10 // in pixels - default is 10
+ }]
+ }, {
+ ptype: "gxp_featuremanager",
+ id: "featuremanager",
+ paging: false,
+ layer: {
+ source: "local",
+ name: "sfpark:spaces"
+ }
+ }, {
+ ptype: "gxp_featureeditor",
+ snappingAgent: "curb-snapping",
+ featureManager: "featuremanager",
+ featureEditPopupType: "app_asseteditorpopup",
+ autoLoadFeatures: true,
+ toggleGroup: this.toggleGroup,
+ actionTarget: {target: "paneltbar", index: 8},
+ tolerance: 6
+ }]
+
+ });
+
+ </script>
+
+
+ </head>
+ <body></body>
+</html>
View
412 app/static/script/app/ParkingManager/AssetEditorPopup.js
@@ -0,0 +1,412 @@
+/**
+ * Copyright (c) 2008-2011 The Open Planning Project
+ *
+ * Published under the BSD license.
+ * See https://github.com/opengeo/gxp/raw/master/license.txt for the full text
+ * of the license.
+ */
+
+/** api: (define)
+ * module = ParkingManager
+ * class = AssetEditorPopup
+ * extends = GeoExt.Popup
+ */
+
+/** api: constructor
+ * .. class:: AssetEditorPopup(config)
+ *
+ * Create a new popup which displays forms for editing parking space
+ * attributes.
+ */
+Ext.namespace("ParkingManager");
+ParkingManager.AssetEditorPopup = Ext.extend(GeoExt.Popup, {
+
+ /** i18n **/
+ closeMsgTitle: 'Save Changes?',
+ closeMsg: 'This feature has unsaved changes. Would you like to save your changes?',
+ deleteMsgTitle: 'Delete Feature?',
+ deleteMsg: 'Are you sure you want to delete this feature?',
+ editButtonText: 'Edit',
+ editButtonTooltip: 'Make this feature editable',
+ deleteButtonText: 'Delete',
+ deleteButtonTooltip: 'Delete this feature',
+ cancelButtonText: 'Cancel',
+ cancelButtonTooltip: 'Stop editing, discard changes',
+ saveButtonText: 'Save',
+ saveButtonTooltip: 'Save changes',
+
+ /** private config overrides **/
+ layout: "fit",
+
+ /** api: config[feature]
+ * ``OpenLayers.Feature.Vector`` The feature to edit and display.
+ */
+
+ /** api: config[vertexRenderIntent]
+ * ``String`` renderIntent for feature vertices when modifying. Undefined
+ * by default.
+ */
+
+ /** api: config[streetViewTitle]
+ * ``String``
+ * Title for street view tab (i18n).
+ */
+ streetViewTitle: "Street View",
+
+ /** api: config[attributeFormTitle]
+ * ``String``
+ * Title for attribute form tab (i18n).
+ */
+ attributeFormTitle: "Space Attributes",
+
+ /** api: config[headingAttribute]
+ * ``String``
+ * Optional feature attribute name with heading information. Values should
+ * be degrees clockwise relative to north. If present, this value will be
+ * used to orient the camera in the street view.
+ */
+ headingAttribute: null,
+
+ /** api: property[feature]
+ * ``OpenLayers.Feature.Vector`` The feature being edited/displayed.
+ */
+ feature: null,
+
+ /** api: config[readOnly]
+ * ``Boolean`` Set to true to disable editing. Default is false.
+ */
+ readOnly: false,
+
+ /** api: config[allowDelete]
+ * ``Boolean`` Set to true to provide a Delete button for deleting the
+ * feature. Default is false.
+ */
+ allowDelete: false,
+
+ /** api: config[editing]
+ * ``Boolean`` Set to true to open the popup in editing mode.
+ * Default is false.
+ */
+
+ /** private: property[editing]
+ * ``Boolean`` If we are in editing mode, this will be true.
+ */
+ editing: false,
+
+ /** private: property[modifyControl]
+ * ``OpenLayers.Control.ModifyFeature`` If in editing mode, we will have
+ * this control for editing the geometry.
+ */
+ modifyControl: null,
+
+ /** private: property[geometry]
+ * ``OpenLayers.Geometry`` The original geometry of the feature we are
+ * editing.
+ */
+ geometry: null,
+
+ /** private: property[attributes]
+ * ``Object`` The original attributes of the feature we are editing.
+ */
+ attributes: null,
+
+ /** private: property[cancelButton]
+ * ``Ext.Button``
+ */
+ cancelButton: null,
+
+ /** private: property[saveButton]
+ * ``Ext.Button``
+ */
+ saveButton: null,
+
+ /** private: property[editButton]
+ * ``Ext.Button``
+ */
+ editButton: null,
+
+ /** private: property[deleteButton]
+ * ``Ext.Button``
+ */
+ deleteButton: null,
+
+ /** private: method[initComponent]
+ */
+ initComponent: function() {
+ this.addEvents(
+ /** api: events[featuremodified]
+ * Fires when the feature associated with this popup has been
+ * modified (i.e. when the user clicks "Save" on the popup) or
+ * deleted (i.e. when the user clicks "Delete" on the popup).
+ *
+ * Listener arguments:
+ * * panel - :class:`ParkingManager.AssetEditorPopup` This popup.
+ * * feature - ``OpenLayers.Feature`` The modified feature.
+ */
+ "featuremodified",
+
+ /** api: events[canceledit]
+ * Fires when the user exits the editing mode by pressing the
+ * "Cancel" button or selecting "No" in the popup's close dialog.
+ *
+ * Listener arguments:
+ * * panel - :class:`ParkingManager.AssetEditorPopup` This popup.
+ * * feature - ``OpenLayers.Feature`` The feature. Will be null
+ * if editing of a feature that was just inserted was cancelled.
+ */
+ "canceledit",
+
+ /** api: events[cancelclose]
+ * Fires when the user answers "Cancel" to the dialog that
+ * appears when a popup with unsaved changes is closed.
+ *
+ * Listener arguments:
+ * * panel - :class:`ParkingManager.AssetEditorPopup` This popup.
+ */
+ "cancelclose"
+ );
+
+ var feature = this.feature;
+ if (!this.location) {
+ this.location = feature
+ };
+
+ this.anchored = !this.editing;
+
+ if (!this.title && feature.fid) {
+ this.title = feature.fid;
+ }
+
+ this.editButton = new Ext.Button({
+ text: this.editButtonText,
+ tooltip: this.editButtonTooltip,
+ iconCls: "edit",
+ handler: this.startEditing,
+ scope: this
+ });
+
+ this.deleteButton = new Ext.Button({
+ text: this.deleteButtonText,
+ tooltip: this.deleteButtonTooltip,
+ iconCls: "delete",
+ hidden: !this.allowDelete,
+ handler: this.deleteFeature,
+ scope: this
+ });
+
+ this.cancelButton = new Ext.Button({
+ text: this.cancelButtonText,
+ tooltip: this.cancelButtonTooltip,
+ iconCls: "cancel",
+ hidden: true,
+ handler: function() {
+ this.stopEditing(false);
+ },
+ scope: this
+ });
+
+ this.saveButton = new Ext.Button({
+ text: this.saveButtonText,
+ tooltip: this.saveButtonTooltip,
+ iconCls: "save",
+ hidden: true,
+ handler: function() {
+ this.stopEditing(true);
+ },
+ scope: this
+ });
+
+ this.items = [{
+ xtype: "tabpanel",
+ border: false,
+ activeTab: 0,
+ items: [{
+ xtype: "panel",
+ title: this.attributeFormTitle,
+ html: "tab 1"
+ }, {
+ xtype: "gxp_googlestreetviewpanel",
+ title: this.streetViewTitle,
+ heading: this.getOrientationForFeature(feature),
+ zoom: 1
+ }]
+ }];
+
+ this.bbar = new Ext.Toolbar({
+ hidden: this.readOnly,
+ items: [
+ this.editButton,
+ this.deleteButton,
+ this.saveButton,
+ this.cancelButton
+ ]
+ });
+
+ ParkingManager.AssetEditorPopup.superclass.initComponent.call(this);
+
+ this.on({
+ "show": function() {
+ if (this.editing) {
+ this.editing = null;
+ this.startEditing();
+ }
+ },
+ "beforeclose": function() {
+ if (!this.editing) {
+ return;
+ }
+ if (this.feature.state === this.getDirtyState()) {
+ Ext.Msg.show({
+ title: this.closeMsgTitle,
+ msg: this.closeMsg,
+ buttons: Ext.Msg.YESNOCANCEL,
+ fn: function(button) {
+ if (button && button !== "cancel") {
+ this.stopEditing(button === "yes");
+ this.close();
+ } else {
+ this.fireEvent("cancelclose", this);
+ }
+ },
+ scope: this,
+ icon: Ext.MessageBox.QUESTION,
+ animEl: this.getEl()
+ });
+ return false;
+ } else {
+ this.stopEditing(false);
+ }
+ },
+ scope: this
+ });
+ },
+
+ /** private: method[getOrientationForFeature]
+ * :arg feature:
+ *
+ * Return the orientation of a feature based on the case insensitive
+ * `headingAttribute` property.
+ */
+ getOrientationForFeature: function(feature) {
+ var orientation = 0;
+ if (this.headingAttribute) {
+ for (var attr in feature.attributes) {
+ if (attr.toUpperCase() === this.headingAttribute.toUpperCase()) {
+ orientation = Number(feature.attributes[attr]);
+ break;
+ }
+ }
+ }
+ return orientation;
+ },
+
+ /** private: method[getDirtyState]
+ * Get the appropriate OpenLayers.State value to indicate a dirty feature.
+ * We don't cache this value because the popup may remain open through
+ * several state changes.
+ */
+ getDirtyState: function() {
+ return this.feature.state === OpenLayers.State.INSERT ?
+ this.feature.state : OpenLayers.State.UPDATE;
+ },
+
+ /** private: method[startEditing]
+ */
+ startEditing: function() {
+ if (!this.editing) {
+ this.editing = true;
+ this.anc && this.unanchorPopup();
+
+ this.editButton.hide();
+ this.deleteButton.hide();
+ this.saveButton.show();
+ this.cancelButton.show();
+
+ this.geometry = this.feature.geometry.clone();
+ this.attributes = Ext.apply({}, this.feature.attributes);
+
+ this.modifyControl = new OpenLayers.Control.ModifyFeature(
+ this.feature.layer,
+ {standalone: true, vertexRenderIntent: this.vertexRenderIntent}
+ );
+ this.feature.layer.map.addControl(this.modifyControl);
+ this.modifyControl.activate();
+ this.modifyControl.selectFeature(this.feature);
+ }
+ },
+
+ /** private: method[stopEditing]
+ * :arg save: ``Boolean`` If set to true, changes will be saved and the
+ * ``featuremodified`` event will be fired.
+ */
+ stopEditing: function(save) {
+ if (this.editing) {
+ //TODO remove the line below when
+ // http://trac.openlayers.org/ticket/2210 is fixed.
+ this.modifyControl.deactivate();
+ this.modifyControl.destroy();
+
+ var feature = this.feature;
+ if (feature.state === this.getDirtyState()) {
+ if (save === true) {
+ this.fireEvent("featuremodified", this, feature);
+ } else if (feature.state === OpenLayers.State.INSERT) {
+ this.editing = false;
+ feature.layer.destroyFeatures([feature]);
+ this.fireEvent("canceledit", this, null);
+ this.close();
+ } else {
+ var layer = feature.layer;
+ layer.drawFeature(feature, {display: "none"});
+ feature.geometry = this.geometry;
+ feature.attributes = this.attributes;
+ this.setFeatureState(null);
+ layer.drawFeature(feature);
+ this.fireEvent("canceledit", this, feature);
+ }
+ }
+
+ if (!this.isDestroyed) {
+ this.cancelButton.hide();
+ this.saveButton.hide();
+ this.editButton.show();
+ this.allowDelete && this.deleteButton.show();
+ }
+
+ this.editing = false;
+ }
+ },
+
+ deleteFeature: function() {
+ Ext.Msg.show({
+ title: this.deleteMsgTitle,
+ msg: this.deleteMsg,
+ buttons: Ext.Msg.YESNO,
+ fn: function(button) {
+ if (button === "yes") {
+ this.setFeatureState(OpenLayers.State.DELETE);
+ this.fireEvent("featuremodified", this, this.feature);
+ this.close();
+ }
+ },
+ scope: this,
+ icon: Ext.MessageBox.QUESTION,
+ animEl: this.getEl()
+ });
+ },
+
+ /** private: method[setFeatureState]
+ * Set the state of this popup's feature and trigger a featuremodified
+ * event on the feature's layer.
+ */
+ setFeatureState: function(state) {
+ this.feature.state = state;
+ var layer = this.feature.layer;
+ layer && layer.events.triggerEvent("featuremodified", {
+ feature: this.feature
+ });
+ }
+});
+
+/** api: xtype = app_asseteditorpopup */
+Ext.reg('app_asseteditorpopup', ParkingManager.AssetEditorPopup);
Please sign in to comment.
Something went wrong with that request. Please try again.