/// <summary> /// Sort styles by the minimum index of their rules. /// This sorts a slice of the styles, so it returns a sorted /// array but does not change the input. /// </summary> /// <param name="styles"></param> /// <param name="env"></param> private static void SortStyles(List <CartoStyle> styles, Env env) { if (styles.Count == 0) { return; } for (var i = 0; i < styles.Count; i++) { CartoStyle style = styles[i]; style.Index = int.MaxValue; for (int b = 0; b < style.Count; b++) { NodeList <CartoRule> rules = style[b].Rules; for (var r = 0; r < rules.Count; r++) { CartoRule rule = rules[r]; if (rule.Index < style.Index) { style.Index = rule.Index; } } } } styles.Sort(delegate(CartoStyle a, CartoStyle b) { return(a.Index.CompareTo(b.Index)); }); }
/// <summary> /// Apply inherited styles from their ancestors to them. /// </summary> /// <param name="definitions"></param> /// <param name="env"></param> /// <returns>an array of arrays is returned, in which each array refers to a specific attachment</returns> private static List <CartoStyle> InheritDefinitions(List <CartoDefinition> definitions, Env env) { // definitions are ordered by specificity, // high (index 0) to low Dictionary <string, CartoStyle> byAttachment = new Dictionary <string, CartoStyle>(); Dictionary <string, SortedDictionary <string, CartoDefinition> > byFilter = new Dictionary <string, SortedDictionary <string, CartoDefinition> >(); List <CartoStyle> result = new List <CartoStyle>(); List <CartoDefinition> current; string attachment; // Evaluate the filters specified by each definition with the given // environment to correctly resolve variable references definitions.ForEach(delegate(CartoDefinition def) { def.Filters.Evaluate(env); }); current = new List <CartoDefinition>(); for (int i = 0; i < definitions.Count; i++) { CartoDefinition defI = definitions[i]; attachment = defI.Attachment; current.Clear(); current.Add(defI); if (!byAttachment.ContainsKey(attachment)) { CartoStyle style = new CartoStyle(); style.Attachment = attachment; byAttachment.Add(attachment, style); byAttachment[attachment].Attachment = attachment; byFilter[attachment] = new SortedDictionary <string, CartoDefinition>(); result.Add(byAttachment[attachment]); } // Iterate over all subsequent rules. for (var j = i + 1; j < definitions.Count; j++) { CartoDefinition defJ = definitions[j]; if (defJ.Attachment == attachment) { // Only inherit rules from the same attachment. current = AddRules(current, defJ, byFilter[attachment], env); } } for (var k = 0; k < current.Count; k++) { byFilter[attachment][current[k].Filters.ToString()] = current[k]; byAttachment[attachment].Add(current[k]); } } return(result); }
public static Map ReadFromFile(string fileContent, string fileName) { string path = Path.GetDirectoryName(fileName); CartoProject cartoProject = null; switch (Path.GetExtension(fileName).ToLower()) { case ".mml": cartoProject = JsonConvert.DeserializeObject <CartoProject>(fileContent); if (cartoProject.Interactivity != null) { try { Dictionary <string, object> dict = JsonConvert.DeserializeObject <Dictionary <string, object> >(cartoProject.Interactivity.ToString()); cartoProject.Interactivity = dict; } catch { } try { bool enabled = JsonConvert.DeserializeObject <bool>(cartoProject.Interactivity.ToString()); cartoProject.Interactivity = enabled; } catch { } } break; case ".yaml": using (StringReader input = new StringReader(fileContent)) { Deserializer deserializer = new Deserializer(namingConvention: new CamelCaseNamingConvention(), ignoreUnmatched: true); var parser = new MergingParser(new YamlDotNet.Core.Parser(input)); cartoProject = deserializer.Deserialize <CartoProject>(new EventReader(parser)); } break; default: throw new Exception("Unknown extension of the CartoCSS project."); } Map map = null; if (cartoProject.Stylesheet != null && cartoProject.Stylesheet.Length > 0 && cartoProject.Layers.Length > 0) { ICartoTranslator cartoTranslator = CartoGeneratorConverterFactory.CreateTranslator(cartoProject.Generator); CartoParser parser = new CartoParser(); parser.NodeProvider = new CartoNodeProvider(); Env env = new Env(); List <Ruleset> ruleSets = new List <Ruleset>(); List <CartoDefinition> definitions = new List <CartoDefinition>(); foreach (string styleName in cartoProject.Stylesheet) { string styleFileName = Path.Combine(path, styleName); try { Ruleset ruleSet = parser.Parse(File.ReadAllText(styleFileName), styleFileName, env); ruleSets.Add(ruleSet); // Get an array of Ruleset objects, flattened // and sorted according to specificitySort var defs = new List <CartoDefinition>(); defs = ruleSet.Flatten(defs, null, env); defs.Sort(new SpecificitySorter()); definitions.AddRange(defs); env.Frames.Push(ruleSet); } catch (Exception ex) { Exception ex2 = new IOException(string.Format("An error occured during parsing of the style '{0}'.", styleFileName) + ex.Message); LogFactory.WriteLogEntry(Logger.Default, ex2); throw ex2; } } string interactivityLayer = null; if (cartoProject.GetInteractivity() != null && cartoProject.GetInteractivity().ContainsKey("layer")) { interactivityLayer = cartoProject.GetInteractivity()["layer"].ToString(); } map = CreateMap(cartoProject, definitions, env, cartoTranslator); foreach (CartoLayer cartoLayer in cartoProject.Layers) { CartoDatasource datasource = cartoLayer.Datasource; StyledLayer styledLayer = CreateStyledLayer(cartoLayer, map, cartoTranslator); try { string[] classes = (cartoLayer.Class.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); Dictionary <string, bool> classIndex = new Dictionary <string, bool>(classes.Length); for (int i = 0; i < classes.Length; i++) { classIndex[classes[i]] = true; } var matching = definitions.FindAll(delegate(CartoDefinition def) { return(def.AppliesTo(cartoLayer.Name, classIndex)); }); if (matching.Count > 0) { List <CartoStyle> rules = InheritDefinitions(matching, env); if (rules.Count > 0) { SortStyles(rules, env); for (int k = 0; k < rules.Count; k++) { CartoStyle cartoStyle = rules[k]; cartoStyle.Fold(env); string styleName = cartoLayer.Name + (cartoStyle.Attachment != "__default__" ? "-" + cartoStyle.Attachment : ""); FeatureTypeStyle style = CreateStyle(styleName, cartoStyle, env, cartoTranslator); if (style.Rules.Count > 0) { styledLayer.Styles.Add(style); } } cartoTranslator.ProcessStyles(styledLayer.Styles); } if (!string.IsNullOrEmpty(interactivityLayer) && interactivityLayer.Equals(styledLayer.Name)) { styledLayer.Enabled = false; } map.AddLayer(styledLayer); } } catch (Exception ex) { Exception ex2 = new IOException(string.Format("Unable to create data source provider with type '{0}' for the layer '{1}'.", datasource.Type, cartoLayer.Name) + ex.Message); LogFactory.WriteLogEntry(Logger.Default, ex2); } } } return(map); }
/// <summary> /// Creates <see cref="FeatureTypeStyle"/> object /// </summary> /// <param name="styleName"></param> /// <param name="cartoStyle"></param> /// <param name="env"></param> /// <param name="cartoTranslator"></param> /// <returns></returns> private static FeatureTypeStyle CreateStyle(string styleName, CartoStyle cartoStyle, Env env, ICartoTranslator cartoTranslator) { FeatureTypeStyle style = new FeatureTypeStyle(styleName); style.ProcessFeatureOnce = true; List <CartoDefinition> definitions = cartoStyle.Definitions; Dictionary <string, int> existingFilters = null; List <string> imageFilters = new List <string>(); List <string> imageFiltersBuffer = new List <string>(); int k = 0; for (int i = 0; i < definitions.Count; i++) { CartoDefinition def = definitions[i]; NodeList <CartoRule> rules = def.Rules; for (int j = 0; j < rules.Count; j++) { CartoRule cartoRule = rules[j]; string ruleName = cartoRule.Name; if (ruleName == "image-filters") { string filter = cartoRule.Value.ToString(); if (!imageFilters.Contains(filter)) { style.ImageFiltersOptions.Filters.Add(cartoTranslator.ToImageFilter(filter)); style.ImageFiltersOptions.Enabled = true; imageFilters.Add(filter); } k++; } else if (ruleName == "image-filters-inflate") { bool inflate = Convert.ToBoolean(cartoRule.Value.ToString()); if (inflate) { style.ImageFiltersOptions.Enabled = true; } k++; } else if (ruleName == "direct-image-filters") { string filter = cartoRule.Value.ToString(); if (!imageFiltersBuffer.Contains(filter)) { style.ImageFiltersOptions.BufferFilters.Add(cartoTranslator.ToImageFilter(filter)); style.ImageFiltersOptions.Enabled = true; imageFiltersBuffer.Add(filter); } k++; } else if (ruleName == "comp-op") { style.BlendingOptions.CompositingMode = cartoTranslator.ToCompositingMode(cartoRule.Value.Evaluate(env).ToString()); k++; } else if (ruleName == "opacity") { style.BlendingOptions.Opacity = (float)(cartoRule.Value.Evaluate(env) as Number).Value; k++; } if (ruleName == "filter-mode") { string filterMode = cartoRule.Value.Evaluate(env).ToCSS(env); if (string.Equals(filterMode, "all")) { style.ProcessFeatureOnce = false; } else if (string.Equals(filterMode, "first")) { style.ProcessFeatureOnce = true; } k++; } } if (k < rules.Count) { if (existingFilters == null) { existingFilters = new Dictionary <string, int>(); } CreateRules(style, def, env, existingFilters, cartoTranslator); } } return(style); }