/* * SOFTWARE NAME: Thematic Mapping Engine * SOFTWARE RELEASE: 1.6 * COPYRIGHT NOTICE: Copyright (C) 2008 Bjorn Sandvik, * bjorn@thematicmapping.org * SOFTWARE LICENSE: GNU General Public License version 3 (GPLv3) * NOTICE: > * This program is free software; you can redistribute it and/or * modify it under the terms of version 3 of the GNU General * Public License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * http://www.gnu.org/licenses/ * */ // Load Google Earth API google.load("earth", "1"); // Define namespaces for Thematic Mapping Engine (TME) // Encapsulate all variables and methods in one global object Ext.namespace('TME'); // Variables for Google Earth API TME.google = { earth: null, // Google Earth instance baseURL: 'http://localhost/thematicmapping/tme/', // Local URL networkLink: null // Link to KMZ file } // Various data stores used by form fields TME.data = { // Indicator store (loaded from server) indicators: new Ext.data.JsonStore({ url: 'TME_FormHandler.php', baseParams: {task: 'indicators'}, root: 'indicators', fields: ['id', 'name', 'description', 'source'], autoLoad: true }), // Year store (loaded from server when indicator is selected) years: new Ext.data.JsonStore({ url: 'TME_FormHandler.php', baseParams: {task:'indYears'}, root: 'years', fields: ['year'] }), // Proportional symbol: Images images: [ ['circle', 'Circle'], ['square', 'Square'] ], // Proportional symbol: Regular polygons polygons: [ ['circle', 'Circle'], ['square', 'Square'] ], // Proportional symbol: 3d objects colladas: [ ['dome', 'Dome'], ['sphere', 'Sphere'], ['cube', 'Cube'] ], shapeStore: new Ext.data.SimpleStore({ fields: ['id', 'name'], data: [] }) } // All form fields TME.field = { // Thematic mapping technique: Prism Radio prism: { xtype: 'radio', boxLabel: 'Prism', hideLabel: true, name: 'mapType', inputValue: 'prism', checked: false, listeners: {focus:{fn:function(field) { TME.field.hideField(TME.field.barSize); TME.field.symbolShape.allowBlank = true; TME.fieldset.symbolStyle.hide(); TME.fieldset.prismStyle.show(); TME.field.showField(TME.field.colourScale); TME.field.showField(TME.field.singleColour); }}} }, choropleth: { xtype: 'radio', boxLabel: 'Choropleth', hideLabel: true, name: 'mapType', inputValue: 'choropleth', checked: true, listeners: {focus:{fn:function(field) { TME.field.symbolShape.allowBlank = true; TME.fieldset.symbolStyle.hide(); TME.fieldset.prismStyle.hide(); TME.field.colourScale.setValue(true); TME.field.hideField(TME.field.colourScale); TME.field.hideField(TME.field.singleColour); TME.field.hideField(TME.field.colour); TME.field.showField(TME.field.startColour); TME.field.showField(TME.field.endColour); TME.field.showLegend.enable(); }}} }, bar: new Ext.form.Radio({ xtype: 'radio', boxLabel: 'Bar', hideLabel: true, name: 'mapType', inputValue: 'bar', checked: false, listeners: {focus:{fn:function(field) { TME.field.showField(TME.field.barSize); TME.field.symbolShape.allowBlank = true; TME.fieldset.symbolStyle.hide(); TME.fieldset.prismStyle.show(); TME.field.showField(TME.field.colourScale); TME.field.showField(TME.field.singleColour); }}} }), // Thematic mapping technique: Proportional symbol Radio symbol: new Ext.form.Radio({ boxLabel: 'Proportional symbol', hideLabel: true, name: 'mapType', inputValue: 'symbol', checked: false, listeners: {focus:{fn:function(field) { TME.fieldset.prismStyle.hide(); TME.fieldset.symbolStyle.show(); TME.field.symbolShape.allowBlank = false; TME.field.showField(TME.field.colourScale); TME.field.showField(TME.field.singleColour); }}} }), // Indicator ComboBox indicator: { xtype: 'combo', fieldLabel: 'Indicator', hiddenName: 'indicator', store: TME.data.indicators, valueField: 'id', displayField: 'name', editable: true, forceSelection: true, mode: 'local', triggerAction: 'all', anchor:'94%', allowBlank: false, emptyText: 'Select an indicator...', listeners: {select:{fn:function(combo, record, index) { // Set default title, description and source TME.field.mapTitle.setValue(record.data.name); TME.field.mapTitle.setDisabled(false); TME.field.mapDescription.setValue(record.data.description); TME.field.mapDescription.setDisabled(false); TME.field.mapSource.setValue('Statistics from ' + record.data.source); TME.field.mapSource.setDisabled(false); // Load available years TME.field.year.setValue(''); TME.field.year.setDisabled(false); TME.field.year.store.reload({ params: { id: combo.getValue() } }); // Buttons are disables before year is selected TME.button.submit.disable(); TME.button.preview.disable(); }}} }, // Year ComboBox year: new Ext.form.ComboBox({ fieldLabel: 'Year', hiddenName: 'year', store: TME.data.years, valueField: 'year', displayField: 'year', editable: true, forceSelection: true, mode: 'local', triggerAction: 'all', anchor: '55%', allowBlank: false, emptyText: 'Select year...', disabled: true, listeners: {select:{fn:function(combo, value) { TME.button.submit.enable(); TME.button.preview.enable(); }}} }), // Proportional symbol: Image imageSymbol: { xtype: 'radio', boxLabel: 'Image', hideLabel: true, checked: true, name: 'symbolType', inputValue: 'image', listeners: {focus:{fn:function(field) { TME.field.symbolShape.reset(); TME.data.shapeStore.removeAll(); TME.data.shapeStore.loadData( TME.data.images ); }}} }, // Proportional symbol: Regular polygon polygonSymbol: { xtype: 'radio', boxLabel: 'Regular polygon', hideLabel: true, checked: false, name: 'symbolType', inputValue: 'polygon', listeners: {focus:{fn:function(field) { TME.field.symbolShape.reset(); TME.data.shapeStore.removeAll(); TME.data.shapeStore.loadData( TME.data.polygons ); }}} }, // Proportional symbol: 3D object colladaSymbol: { xtype: 'radio', boxLabel: 'Collada (3d)', hideLabel: true, checked: false, name: 'symbolType', inputValue: 'collada', listeners: {focus:{fn:function(field) { TME.field.symbolShape.reset(); TME.data.shapeStore.removeAll(); TME.data.shapeStore.loadData( TME.data.colladas ); }}} }, // Proportional symbol: shape symbolShape: new Ext.form.ComboBox({ fieldLabel: 'Shape', hiddenName: 'symbolShape', store: TME.data.shapeStore, valueField: 'id', displayField: 'name', emptyText: 'Select shape...', editable: false, forceSelection: true, mode: 'local', triggerAction: 'all', anchor:'94%' }), barSize: new Ext.form.NumberField({ xtype: 'numberfield', fieldLabel: 'Bar radius', name: 'barSize', value: 50000, width: 77, allowDecimals: false, allowNegative: false, allowBlank: false }), // Max sixe NumberField size: new Ext.form.NumberField({ xtype: 'numberfield', fieldLabel: 'Max size', name: 'symbolMaxSize', value: '5', width: 30, allowDecimals: false, allowNegative: false, allowBlank: false }), // Max height NumberField maxHeight: new Ext.form.NumberField({ fieldLabel: 'Max height', name: 'maxHeight', value: '2000000', width: 80, allowDecimals: false, allowNegative: false, allowBlank: false }), // Map title TextFiled mapTitle: new Ext.form.TextField({ fieldLabel: 'Title', name: 'title', allowBlank: false }), // Map description TextField mapDescription: new Ext.form.TextArea({ fieldLabel: 'Description', name: 'description' }), // Map source TextField mapSource: new Ext.form.TextField({ fieldLabel: 'Source', name: 'source', allowBlank: false }), colourScale: new Ext.form.Radio({ boxLabel: 'Colour scale', hideLabel: true, name: 'colourType', inputValue: 'scale', checked: true, listeners: {focus:{fn:function(field) { TME.field.hideField(TME.field.colour); TME.field.showField(TME.field.startColour); TME.field.showField(TME.field.endColour); TME.field.showLegend.enable(); TME.classification.show(); }}} }), singleColour: new Ext.form.Radio({ boxLabel: 'Single colour', hideLabel: true, name: 'colourType', inputValue: 'single', checked: false, listeners: {focus:{fn:function(field) { TME.field.showField(TME.field.colour); TME.field.hideField(TME.field.startColour); TME.field.hideField(TME.field.endColour); TME.field.showLegend.disable(); TME.classification.hide(); }}} }), // Colour ColorField (extension) colour: new Ext.form.ColorField({ fieldLabel: 'Colour', name: 'colour', defaultColor: 'FF6600', width: 80, showHexValue: false }), // Start colour ColorField (extension) startColour: new Ext.form.ColorField({ fieldLabel: 'Start colour', name: 'startColour', defaultColor: 'FFFF99', anchor:'90%', showHexValue: false }), // End colour ColorField (extension) endColour: new Ext.form.ColorField({ fieldLabel: 'End colour', name: 'endColour', defaultColor: 'FF6600', anchor:'90%', showHexValue: false }), // No data colour ColorField (extension) noDataColour: new Ext.form.ColorField({ fieldLabel: 'No value', name: 'noDataColour', defaultColor: 'C0C0C0', anchor:'90%', showHexValue: false }), // Classification: Unclassed unclassed: new Ext.form.Radio({ boxLabel: 'Unclassed', hideLabel: true, name: 'classification', inputValue: 'unclassed', checked: true, listeners: {focus:{fn:function(field) { TME.field.hideField(TME.field.numClasses); }}} }), // Classification: Equal interval equalInterval: new Ext.form.Radio({ boxLabel: 'Equal intervals', hideLabel: true, name: 'classification', inputValue: 'equal', checked: false, listeners: {focus:{fn:function(field) { TME.field.showField(TME.field.numClasses); }}} }), // Classification: Quantiles quantile: new Ext.form.Radio({ boxLabel: 'Quantiles', hideLabel: true, name: 'classification', inputValue: 'quantile', checked: false, listeners: {focus:{fn:function(field) { TME.field.showField(TME.field.numClasses); }}} }), // Number of classes numClasses: new Ext.form.NumberField({ fieldLabel: 'Classes', name: 'numClasses', value: 5, maxValue: 9, minValue: 2, width: 22, allowDecimals: false, allowBlank: false }), // Time: One year singleYear: { xtype: 'radio', boxLabel: 'Single year', hideLabel: true, name: 'timeType', inputValue: 'year', checked: true, listeners: {focus:{fn:function(field) { TME.field.quantile.enable(); if (TME.field.year.value) { TME.button.preview.enable(); } }}} }, // Time: Time series timeSeries: { xtype: 'radio', boxLabel: 'Time series', hideLabel: true, name: 'timeType', inputValue: 'series', checked: false, listeners: {focus:{fn:function(field) { if (TME.field.quantile.getValue()) { // Not valid for time series TME.field.equalInterval.setValue(true); } TME.field.quantile.disable(); TME.button.preview.disable(); }}} }, // Time: Time slider timeSlider: { xtype: 'radio', boxLabel: 'Time slider', hideLabel: true, name: 'timeType', inputValue: 'slider', checked: false, listeners: {focus:{fn:function(field) { if (TME.field.quantile.getValue()) { // Not valid for time series TME.field.equalInterval.setValue(true); } TME.field.quantile.disable(); TME.button.preview.disable(); }}} }, // Show title showTitle: new Ext.form.Checkbox({ boxLabel: 'Show title & source', hideLabel: true, checked: true, name: 'showTitle' }), // Show legend showLegend: new Ext.form.Checkbox({ boxLabel: 'Show colour legend', hideLabel: true, checked: true, name: 'showLegend' }), // Show values showValues: new Ext.form.Checkbox({ boxLabel: 'Show values', hideLabel: true, name: 'showValues' }), // Show names showNames: new Ext.form.Checkbox({ boxLabel: 'Show names', hideLabel: true, name: 'showNames' }), // Max height NumberField opacity: new Ext.form.NumberField({ fieldLabel: 'Opacity', name: 'opacity', value: 90, maxValue: 100, width: 80, allowDecimals: false, allowNegative: false, allowBlank: false }), // METHODS // Hide form field hideField: function(field){ field.hide(); field.getEl().up('.x-form-item').setDisplayed(false); // hide label }, // Show form field showField: function(field){ field.show(); field.getEl().up('.x-form-item').setDisplayed(true);// show label } } // Classification FieldSet TME.classification = new Ext.form.FieldSet({ title: 'Classification', collapsible: false, autoHeight: true, labelWidth: 65, style: 'margin-top: 10px', items: [{ layout: 'column', defaults: {layout: 'form'}, items:[ {items: [TME.field.unclassed, TME.field.numClasses], columnWidth: .32}, {items: [TME.field.equalInterval], columnWidth: .4}, {items: [TME.field.quantile], columnWidth: .28} ] }] }); // Grouping fields in fieldsets TME.fieldset = { // Symbol fieldset (column) technique: new Ext.form.FieldSet({ title: 'Technique', collapsible: false, autoHeight: true, labelWidth: 65, items: [{ layout: 'column', defaults: {layout: 'form'}, items:[ {items: [TME.field.choropleth], columnWidth: .27}, {items: [TME.field.prism], columnWidth: .19}, {items: [TME.field.bar], columnWidth: .17}, {items: [TME.field.symbol], columnWidth: .37} ] }] }), // Statistics FieldSet (title, description source) statistics: new Ext.form.FieldSet({ title: 'Statistics', collapsible: false, autoHeight: true, items: [ TME.field.indicator, TME.field.year ] }), // Description FieldSet (title, description source) description: new Ext.form.FieldSet({ title: 'Map description', collapsible: true, autoHeight: true, collapsed: true, defaults: {anchor: '94%', disabled: true}, items: [ TME.field.mapTitle, TME.field.mapDescription, TME.field.mapSource ] }), // Prism style FieldSet prismStyle: new Ext.form.FieldSet({ title: 'Prism / bar style', collapsible: true, autoHeight: true, items: [ { layout:'column', items:[{ columnWidth:.5, layout: 'form', items: [ TME.field.maxHeight ] },{ columnWidth:.5, layout: 'form', items: [ TME.field.barSize ] }] }] }), // Symbol style FieldSet symbolStyle: new Ext.form.FieldSet({ title: 'Symbol style', collapsible: true, autoHeight: true, items: [ { layout: 'column', style: 'padding-bottom: 10px', defaults: {layout: 'form'}, items:[ {items: [TME.field.imageSymbol], columnWidth: .3}, {items: [TME.field.polygonSymbol], columnWidth: .4}, {items: [TME.field.colladaSymbol], columnWidth: .3} ] }, TME.field.symbolShape, { layout: 'column', defaults: {layout: 'form'}, items:[ {items: [TME.field.size], columnWidth: .5} ] } ] }), // Colour FieldSet colours: new Ext.form.FieldSet({ title: 'Colours', collapsible: true, autoHeight: true, style: 'padding-bottom: 0px', items: [{ layout: 'column', style: 'padding-bottom: 10px', defaults: {layout: 'form'}, items:[ {items: [TME.field.colourScale], columnWidth: .5}, {items: [TME.field.singleColour], columnWidth: .5} ] }, { layout:'column', items:[{ columnWidth:.5, layout: 'form', items: [ TME.field.colour, TME.field.startColour, TME.field.endColour ] },{ columnWidth:.5, layout: 'form', items: [ TME.field.noDataColour, TME.field.opacity ] } ] }, TME.classification ] }), // Time FieldSet time: new Ext.form.FieldSet({ title: 'Time', collapsible: true, autoHeight: true, labelWidth: 65, items: [{ layout: 'column', defaults: {layout: 'form'}, items:[ {items: [TME.field.singleYear], columnWidth: .35}, {items: [TME.field.timeSeries], columnWidth: .35}, {items: [TME.field.timeSlider], columnWidth: .30} ] }] }), // Display FieldSet display: new Ext.form.FieldSet({ title: 'Display', collapsible: true, collapsed: false, autoHeight: true, labelWidth: 65, items: [ { layout:'column', items:[{ columnWidth:.5, layout: 'form', items: [ TME.field.showTitle, TME.field.showLegend ] },{ columnWidth:.5, layout: 'form', items: [ TME.field.showValues, TME.field.showNames ] }] }] }) } TME.engine = new Ext.FormPanel({ labelWidth: 70, frame: true, bodyStyle: 'padding:10px 10px 0', width: 400, items: [ TME.fieldset.statistics, TME.fieldset.technique, TME.fieldset.symbolStyle, TME.fieldset.prismStyle, TME.fieldset.colours, TME.fieldset.time, TME.fieldset.display, TME.fieldset.description ] }); // Form buttons TME.button = { // Preview button preview: TME.engine.addButton({ text: 'Preview *', disabled: true, handler: function(){ TME.engine.form.submit({ url:'TME_FormHandler.php', params: {task: 'makeKMZ'}, waitMsg:'Generating KML...', success: function(form, action) { // Check if GE plugin if (TME.google.earth) { // Link to KMZ file var kmzlink = TME.google.baseURL + action.result.file; // Remove existing KML (if exists) if (TME.google.networkLink) { TME.google.earth.getGlobe().getFeatures().removeChild(TME.google.networkLink); TME.google.networkLink = null; } // Show borders if bar or proportional symbol map TME.google.earth.getLayerRoot().enableLayerById(TME.google.earth.LAYER_BORDERS, false); if (TME.field.symbol.getValue() || TME.field.bar.getValue()) { TME.google.earth.getLayerRoot().enableLayerById(TME.google.earth.LAYER_BORDERS, true); } // Load KML TME.google.networkLink = TME.google.earth.createNetworkLink(""); TME.google.networkLink.setFlyToView(true); var link = TME.google.earth.createLink(""); link.setHref(kmzlink); TME.google.networkLink.setLink(link); // Add KML to GE plugin and show window TME.google.earth.getGlobe().getFeatures().appendChild(TME.google.networkLink); TME.google.earth.getWindow().setVisibility(true); } TME.window.previw.show(); }, failure: function(form, action) { Ext.MessageBox.alert('Error', 'Engine failed'); } }); } }), // Submit button submit: TME.engine.addButton({ text: 'Download', disabled: true, handler: function(){ TME.engine.form.submit({ url:'TME_FormHandler.php', params: {task: 'makeKMZ'}, waitMsg:'Generating KML...', success: function (form, action) { Ext.Msg.buttonText.cancel = 'Close'; Ext.Msg.show({ title: 'Download', msg: '
Click here to download the file to your computer
', buttons: Ext.Msg.CANCEL, icon: Ext.Msg.INFO, minWidth: 350 }); }, failure: function(form, action) { Ext.MessageBox.alert('Error', 'Engine failed'); } }); } }) } TME.window = { previw: new Ext.Window({ layout: 'fit', width: 660, height: 530, shadow: true, title: 'Preview KML', html: '
', closeAction: 'hide', modal: true, draggable: false }) } // Create application // Anonymous function (function without name) that runs immediately after parsing TME.app = function() { // Do NOT access DOM from here; elements don't exist yet // Private variables (not visible outside the application) var kmzURL; // URL to KMZ genereated by server // Public space return { // Public method (returned by the anonymous function) init: function() { Ext.QuickTips.init(); // Turn on validation errors beside the field globally Ext.form.Field.prototype.msgTarget = 'side'; // Need to show before GE instance can be created - better way? TME.window.previw.show(); google.earth.createInstance( "map3d", function(object) { TME.google.earth = object; }, function(object) {} ); TME.window.previw.hide(); var form = Ext.get('tme'); TME.engine.render(form); // Default field status TME.fieldset.prismStyle.hide(); TME.fieldset.symbolStyle.hide(); TME.field.hideField(TME.field.colourScale); TME.field.hideField(TME.field.singleColour); TME.field.hideField(TME.field.colour); TME.field.hideField(TME.field.numClasses); TME.data.shapeStore.loadData( TME.data.images ); } }; }(); // end of app Ext.onReady(TME.app.init, TME.app);