using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Xml; using System.Globalization; #pragma warning disable 1591 namespace OSGeo.MapGuide.Viewer { public partial class MgThemeControlImpl : MgControlView { private IMapViewer _viewer; private BindingList _properties; private BindingList _layers; private MgResourceService _resSvc; private string _sessionId; private Dictionary _distros; public MgThemeControlImpl(IMapViewer viewer) { InitializeComponent(); this.Title = Strings.TitleTheme; this.Disposed += new EventHandler(OnDisposed); _viewer = viewer; _properties = new BindingList(); _layers = new BindingList(); numRules.Minimum = Int32.MinValue; numRules.Maximum = Int32.MaxValue; _distros = new Dictionary() { { THEME_INDIVIDUAL, Strings.ThemeIndividual }, { THEME_EQUAL, Strings.ThemeEqual }, { THEME_STDDEV, Strings.ThemeStandardDeviation }, { THEME_QUANT, Strings.ThemeQuantile }, { THEME_JENK, Strings.ThemeJenks } }; cmbLayer.DataSource = _layers; cmbProperty.DataSource = _properties; cmbDistribution.DataSource = GetDistroValues(THEME_INDIVIDUAL, THEME_EQUAL, THEME_STDDEV, THEME_QUANT, THEME_JENK); var map = viewer.GetMap(); var layers = map.GetLayers(); //TODO: Obviously support point and line layers for (var i = 0; i < layers.GetCount(); i++) { var layer = layers.GetItem(i); var cls = layer.GetClassDefinition(); var geom = layer.GetFeatureGeometryName(); if (string.IsNullOrEmpty(geom)) continue; var clsProps = cls.GetProperties(); var geomProp = clsProps.GetItem(geom) as MgGeometricPropertyDefinition; if ((geomProp.GeometryTypes & MgFeatureGeometricType.Surface) == MgFeatureGeometricType.Surface) { _layers.Add(layer); } } cmbLayer.SelectedIndex = 0; cmbLayer_SelectedIndexChanged(this, EventArgs.Empty); cmbDistribution.SelectedIndex = 0; _sessionId = Guid.NewGuid().ToString(); } private KeyValuePair[] GetDistroValues(params string[] distroKeys) { var values = new List>(); foreach (var key in distroKeys) { if (_distros.ContainsKey(key)) values.Add(new KeyValuePair(key, _distros[key])); } return values.ToArray(); } void OnDisposed(object sender, EventArgs e) { _properties.Clear(); _layers.Clear(); if (_resSvc != null) { _resSvc.Dispose(); _resSvc = null; } } private void cmbLayer_SelectedIndexChanged(object sender, EventArgs e) { var layer = cmbLayer.SelectedItem as MgLayerBase; if (layer != null) { var cls = layer.GetClassDefinition(); var clsProps = cls.GetProperties(); _properties.Clear(); for (var i = 0; i < clsProps.GetCount(); i++) { var prop = clsProps.GetItem(i); if (prop.GetPropertyType() == MgFeaturePropertyType.DataProperty) { _properties.Add((MgDataPropertyDefinition)prop); } } cmbProperty.SelectedIndex = 0; cmbProperty_SelectedIndexChanged(this, EventArgs.Empty); cmbScaleRange.DataSource = GetScaleRanges(layer); } } private string[] GetScaleRanges(MgLayerBase layer) { if (_resSvc == null) { var provider = _viewer.GetProvider(); _resSvc = (MgResourceService)provider.CreateService(MgServiceType.ResourceService); } MgResourceIdentifier layerDefResId = layer.GetLayerDefinition(); MgByteReader byteReader = _resSvc.GetResourceContent(layerDefResId); XmlDocument doc = new XmlDocument(); doc.LoadXml(byteReader.ToString()); XmlNodeList nodeList = doc.GetElementsByTagName("VectorScaleRange"); //NOXLATE var scaleRanges = new List(); foreach (XmlElement node in nodeList) { String range = null; XmlNodeList minNodeList = node.GetElementsByTagName("MinScale"); //NOXLATE if (minNodeList.Count > 0) { range = minNodeList.Item(0).FirstChild.Value; } else { range = "0"; //NOXLATE } XmlNodeList maxNodeList = node.GetElementsByTagName("MaxScale"); //NOXLATE if (maxNodeList.Count > 0) { range = range + " - " + maxNodeList.Item(0).FirstChild.Value; //NOXLATE } else { range = range + " - " + Strings.Infinity; //NOXLATE } scaleRanges.Add(range); } return scaleRanges.ToArray(); } private int featureCount; private int ruleCount = 8; private void cmbDistribution_SelectedIndexChanged(object sender, EventArgs e) { if (cmbDistribution.SelectedIndex == 0) { numRules.Enabled = false; numRules.Value = featureCount; } else { numRules.Enabled = true; numRules.Value = ruleCount; } } private void cmbProperty_SelectedIndexChanged(object sender, EventArgs e) { var layer = cmbLayer.SelectedItem as MgLayerBase; var prop = cmbProperty.SelectedItem as MgDataPropertyDefinition; if (layer != null && prop != null) { SetPropertyMinMaxCount(layer, prop); cmbDistribution.DataSource = GetDistributionsForDataType(prop.DataType); } } private void btnFromFillColor_Click(object sender, EventArgs e) { if (colorDialog.ShowDialog() == DialogResult.OK) { pnlFromFillColor.BackColor = colorDialog.Color; } } private void btnToFillColor_Click(object sender, EventArgs e) { if (colorDialog.ShowDialog() == DialogResult.OK) { pnlToFillColor.BackColor = colorDialog.Color; } } private void btnFromBorderColor_Click(object sender, EventArgs e) { if (colorDialog.ShowDialog() == DialogResult.OK) { pnlFromBorderColor.BackColor = colorDialog.Color; } } private void btnToBorderColor_Click(object sender, EventArgs e) { if (colorDialog.ShowDialog() == DialogResult.OK) { pnlToBorderColor.BackColor = colorDialog.Color; } } private void btnApply_Click(object sender, EventArgs e) { var tp = new ThemeParams() { borderFrom = Util.ToHtmlColor(pnlFromBorderColor.BackColor), borderTo = Util.ToHtmlColor(pnlToBorderColor.BackColor), distro = cmbDistribution.SelectedValue.ToString(), fillFrom = Util.ToHtmlColor(pnlFromFillColor.BackColor), fillTo = Util.ToHtmlColor(pnlToFillColor.BackColor), fillTrans = Convert.ToInt32(numFillTransparency.Value), layer = (MgLayerBase)cmbLayer.SelectedItem, maxValue = txtMax.Text, minValue = txtMin.Text, numRules = Convert.ToInt32(numRules.Value), property = (MgDataPropertyDefinition)cmbProperty.SelectedItem, scaleRangeIndex = cmbScaleRange.SelectedIndex, themeName = txtThemeName.Text }; string name = ApplyTheme(tp); _viewer.RefreshMap(); MessageBox.Show(string.Format(Strings.MsgThemeLayerCreated, name)); } private void SetPropertyMinMaxCount(MgLayerBase layer, MgDataPropertyDefinition prop) { var provider = _viewer.GetProvider(); var featureService = (MgFeatureService)provider.CreateService(MgServiceType.FeatureService); MgResourceIdentifier resId = new MgResourceIdentifier(layer.GetFeatureSourceId()); String minValue = null; String maxValue = null; int count = 0; MgFeatureQueryOptions queryOptions = new MgFeatureQueryOptions(); queryOptions.AddFeatureProperty(prop.Name); MgFeatureReader featureReader = featureService.SelectFeatures(resId, layer.GetFeatureClassName(), queryOptions); Dictionary bucket = new Dictionary(); while (featureReader.ReadNext()) { String value = Util.GetFeaturePropertyValue(featureReader, prop.Name); bucket[value] = value; int propertyType = featureReader.GetPropertyType(prop.Name); if (count == 0) { maxValue = value; minValue = value; } switch (propertyType) { case MgPropertyType.String: if (value.Length > 0) { if (value.CompareTo(maxValue) > 0) maxValue = value; if (value.CompareTo(minValue) < 0) minValue = value; } break; case MgPropertyType.Byte: case MgPropertyType.Int16: case MgPropertyType.Int32: case MgPropertyType.Int64: if (value.Length > 0) { if (Int64.Parse(value) > Int64.Parse(maxValue)) maxValue = value; if (Int64.Parse(value) < Int64.Parse(minValue)) minValue = value; } break; case MgPropertyType.Single: case MgPropertyType.Double: if (value != null) { if (Double.Parse(value) > Double.Parse(maxValue)) maxValue = value; if (Double.Parse(value) < Double.Parse(minValue)) minValue = value; } count++; break; case MgPropertyType.Boolean: case MgPropertyType.DateTime: case MgPropertyType.Null: case MgPropertyType.Blob: case MgPropertyType.Clob: case MgPropertyType.Feature: case MgPropertyType.Geometry: break; } count++; } featureReader.Close(); txtMin.Text = minValue; txtMax.Text = maxValue; if (THEME_INDIVIDUAL == cmbDistribution.SelectedValue.ToString()) count = bucket.Count; numRules.Value = featureCount = count; } const string THEME_INDIVIDUAL = "INDIV_DIST"; //NOXLATE const string THEME_EQUAL = "EQUAL_DIST"; //NOXLATE const string THEME_STDDEV = "STDEV_DIST"; //NOXLATE const string THEME_QUANT = "QUANT_DIST"; //NOXLATE const string THEME_JENK = "JENK_DIST"; //NOXLATE class ThemeParams { public MgLayerBase layer; public MgDataPropertyDefinition property; public int scaleRangeIndex; public int numRules; public string distro; public string fillFrom; public string fillTo; public string borderFrom; public string borderTo; public int fillTrans; public string minValue; public string maxValue; public string themeName; //public string layerName; } private string ApplyTheme(ThemeParams themeParams) { var provider = _viewer.GetProvider(); var map = _viewer.GetMap(); var layers = map.GetLayers(); MgResourceService resourceService = (MgResourceService)provider.CreateService(MgServiceType.ResourceService); MgFeatureService featureService = (MgFeatureService)provider.CreateService(MgServiceType.FeatureService); MgResourceIdentifier resId = new MgResourceIdentifier(themeParams.layer.GetFeatureSourceId()); MgResourceIdentifier layerDefResId = themeParams.layer.GetLayerDefinition(); MgByteReader byteReader = resourceService.GetResourceContent(layerDefResId); // Load the Layer Definition and Navigate to the specified XmlDocument doc = new XmlDocument(); String xmlLayerDef = byteReader.ToString(); doc.LoadXml(xmlLayerDef); XmlNodeList nodeList = doc.GetElementsByTagName("VectorScaleRange"); //NOXLATE XmlElement vectorScaleRangecElement = (XmlElement)nodeList.Item(themeParams.scaleRangeIndex); XmlElement areaTypeStyle = (XmlElement)vectorScaleRangecElement.GetElementsByTagName("AreaTypeStyle").Item(0); //NOXLATE // Remove any existing elements. XmlNodeList areaRuleList = areaTypeStyle.GetElementsByTagName("AreaRule"); //NOXLATE int count = areaRuleList.Count; for (int i = 0; i < count; i++) { //The areaRuleList shrinks as we remove items, so always //remove the first item (don't use the index i) areaTypeStyle.RemoveChild(areaRuleList.Item(0)); } // Now create the new elements. String areaRuleTemplate = Properties.Resources.AreaRuleTemplate; MgFeatureAggregateOptions aggregateOptions = new MgFeatureAggregateOptions(); String value = null; String filterText = null; String areaRuleXML = null; XmlDocument areaDoc = null; XmlNode areaNode = null; double portion = 0.0; double increment = (themeParams.numRules > 1) ? 1.0 / (themeParams.numRules - 1) : 1.0; if (THEME_INDIVIDUAL == themeParams.distro) { aggregateOptions.AddComputedProperty("THEME_VALUE", "UNIQUE(\"" + themeParams.property.Name + "\")"); //NOXLATE MgDataReader dataReader = featureService.SelectAggregate(resId, themeParams.layer.GetFeatureClassName(), aggregateOptions); while (dataReader.ReadNext()) { value = Util.GetFeaturePropertyValue(dataReader, "THEME_VALUE"); //NOXLATE filterText = """ + themeParams.property.Name + "" = "; //NOXLATE if (themeParams.property.DataType == MgPropertyType.String) filterText = filterText + "'" + value + "'"; //NOXLATE else filterText = filterText + value; areaRuleXML = String.Format(areaRuleTemplate, themeParams.property.Name + ":" + value, //NOXLATE filterText, Util.InterpolateColor(portion, themeParams.fillFrom, themeParams.fillTo, themeParams.fillTrans), Util.InterpolateColor(portion, themeParams.borderFrom, themeParams.borderTo, 0)); areaDoc = new XmlDocument(); areaDoc.LoadXml(areaRuleXML); areaNode = doc.ImportNode(areaDoc.DocumentElement, true); areaTypeStyle.AppendChild(areaNode); portion = portion + increment; } dataReader.Close(); } else { var values = new List(); var expr = themeParams.distro + "(\"" + themeParams.property.Name + "\"," + themeParams.numRules + "," + themeParams.minValue + "," + themeParams.maxValue + ")"; //NOXLATE aggregateOptions.AddComputedProperty("THEME_VALUE", expr); //NOXLATE MgDataReader dataReader = featureService.SelectAggregate(resId, themeParams.layer.GetFeatureClassName(), aggregateOptions); while (dataReader.ReadNext()) { value = Util.GetFeaturePropertyValue(dataReader, "THEME_VALUE"); //NOXLATE values.Add(value); } dataReader.Close(); for (int i = 0; i < values.Count - 1; i++) { filterText = """ + themeParams.property.Name + "" >= " + values[i] + " AND "" + themeParams.property.Name; //NOXLATE if (i == values.Count - 1) filterText = filterText + "" <= " + values[i + 1]; //NOXLATE else filterText = filterText + "" < " + values[i + 1]; //NOXLATE areaRuleXML = String.Format(areaRuleTemplate, themeParams.property.Name + ":" + values[i] + " - " + values[i + 1], //NOXLATE filterText, Util.InterpolateColor(portion, themeParams.fillFrom, themeParams.fillTo, themeParams.fillTrans), Util.InterpolateColor(portion, themeParams.borderFrom, themeParams.borderTo, 0)); areaDoc = new XmlDocument(); areaDoc.LoadXml(areaRuleXML); areaNode = doc.ImportNode(areaDoc.DocumentElement, true); areaTypeStyle.AppendChild(areaNode); portion = portion + increment; } } // Now save our new layer definition to the session and add it to the map. String xmlString = doc.DocumentElement.OuterXml; String uniqueName = Util.MakeUniqueLayerName(map, themeParams.layer.Name, themeParams.themeName); String legendLabel = themeParams.layer.GetLegendLabel(); if (!string.IsNullOrEmpty(themeParams.themeName)) legendLabel = legendLabel + " (" + themeParams.themeName + ")"; //NOXLATE MgResourceIdentifier layerResId = new MgResourceIdentifier("Session:" + _sessionId + "//" + uniqueName + ".LayerDefinition"); //NOXLATE resourceService.SetResource(layerResId, new MgByteReader(xmlString, "text/xml"), null); //NOXLATE var newLayer = provider.CreateLayer(layerResId); newLayer.SetName(uniqueName); newLayer.SetLegendLabel(legendLabel); newLayer.SetDisplayInLegend(themeParams.layer.GetDisplayInLegend()); newLayer.SetVisible(true); newLayer.SetSelectable(themeParams.layer.GetSelectable()); //HACK: This has to be true otherwise owner-drawn nodes will not display its children (the theme rules) provider.SetLayerExpandInLegend(newLayer, true); layers.Insert(layers.IndexOf(themeParams.layer), newLayer); //map.Save(resourceService); return uniqueName; } private KeyValuePair[] GetDistributionsForDataType(int type) { var distroTypes = new List(); switch (type) { case MgPropertyType.String: distroTypes.Add(THEME_INDIVIDUAL); break; case MgPropertyType.Byte: case MgPropertyType.Int16: case MgPropertyType.Int32: case MgPropertyType.Int64: distroTypes.Add(THEME_INDIVIDUAL); distroTypes.Add(THEME_EQUAL); distroTypes.Add(THEME_STDDEV); distroTypes.Add(THEME_QUANT); distroTypes.Add(THEME_JENK); break; case MgPropertyType.Single: case MgPropertyType.Double: distroTypes.Add(THEME_EQUAL); distroTypes.Add(THEME_STDDEV); distroTypes.Add(THEME_QUANT); distroTypes.Add(THEME_JENK); break; case MgPropertyType.Boolean: case MgPropertyType.DateTime: case MgPropertyType.Blob: case MgPropertyType.Clob: case MgPropertyType.Feature: case MgPropertyType.Geometry: case MgPropertyType.Null: break; } return GetDistroValues(distroTypes.ToArray()); } } }