Generated-output differences

quicktype output changed between the PR base and tested PR merge revisions.
← Back to the pull request
17test cases
17files differ
0modified
17new
0deleted
12,997changed lines
+12,997 −0insertions / deletions
Base d3014aaec4f7ed11fb83168f39211b755deeeb1d · PR merge 5ba8cea33a97633fb30c8cccdbca880dcae42a2d · Head 726c8e950e7f1dd1fc093c36a638c3b7a06cfe4e · raw patch
Test case

test/inputs/schema/all-of-additional-properties-false.schema

1 generated file · +109 −0
Aschema-swiftdefault / quicktype.swift+109 −0
@@ -0,0 +1,109 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let amount: Double
11+ let frequency: Frequency
12+ let description: String?
13+ let type: TypeEnum
14+
15+ enum CodingKeys: String, CodingKey {
16+ case amount = "amount"
17+ case frequency = "frequency"
18+ case description = "description"
19+ case type = "type"
20+ }
21+}
22+
23+// MARK: TopLevel convenience initializers and mutators
24+
25+extension TopLevel {
26+ init(data: Data) throws {
27+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
28+ }
29+
30+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
31+ guard let data = json.data(using: encoding) else {
32+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
33+ }
34+ try self.init(data: data)
35+ }
36+
37+ init(fromURL url: URL) throws {
38+ try self.init(data: try Data(contentsOf: url))
39+ }
40+
41+ func with(
42+ amount: Double? = nil,
43+ frequency: Frequency? = nil,
44+ description: String?? = nil,
45+ type: TypeEnum? = nil
46+ ) -> TopLevel {
47+ return TopLevel(
48+ amount: amount ?? self.amount,
49+ frequency: frequency ?? self.frequency,
50+ description: description ?? self.description,
51+ type: type ?? self.type
52+ )
53+ }
54+
55+ func jsonData() throws -> Data {
56+ return try newJSONEncoder().encode(self)
57+ }
58+
59+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
60+ return String(data: try self.jsonData(), encoding: encoding)
61+ }
62+}
63+
64+enum Frequency: String, Codable {
65+ case weekly = "Weekly"
66+ case monthly = "Monthly"
67+ case annually = "Annually"
68+}
69+
70+enum TypeEnum: String, Codable {
71+ case grossSalaryAmount = "GrossSalaryAmount"
72+ case overtime = "Overtime"
73+}
74+
75+// MARK: - Helper functions for creating encoders and decoders
76+
77+func newJSONDecoder() -> JSONDecoder {
78+ let decoder = JSONDecoder()
79+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
80+ let container = try decoder.singleValueContainer()
81+ let dateStr = try container.decode(String.self)
82+
83+ let formatter = DateFormatter()
84+ formatter.calendar = Calendar(identifier: .iso8601)
85+ formatter.locale = Locale(identifier: "en_US_POSIX")
86+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
87+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
88+ if let date = formatter.date(from: dateStr) {
89+ return date
90+ }
91+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
92+ if let date = formatter.date(from: dateStr) {
93+ return date
94+ }
95+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
96+ })
97+ return decoder
98+}
99+
100+func newJSONEncoder() -> JSONEncoder {
101+ let encoder = JSONEncoder()
102+ let formatter = DateFormatter()
103+ formatter.calendar = Calendar(identifier: .iso8601)
104+ formatter.locale = Locale(identifier: "en_US_POSIX")
105+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
106+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
107+ encoder.dateEncodingStrategy = .formatted(formatter)
108+ return encoder
109+}
Test case

test/inputs/schema/class-map-union.schema

1 generated file · +172 −0
Aschema-swiftdefault / quicktype.swift+172 −0
@@ -0,0 +1,172 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let union: [String: UnionValue]?
11+
12+ enum CodingKeys: String, CodingKey {
13+ case union = "union"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ union: [String: UnionValue]?? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ union: union ?? self.union
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+enum UnionValue: Codable {
53+ case bool(Bool)
54+ case double(Double)
55+ case string(String)
56+ case unionClass(UnionClass)
57+
58+ init(from decoder: Decoder) throws {
59+ let container = try decoder.singleValueContainer()
60+ if let x = try? container.decode(Bool.self) {
61+ self = .bool(x)
62+ return
63+ }
64+ if let x = try? container.decode(Double.self) {
65+ self = .double(x)
66+ return
67+ }
68+ if let x = try? container.decode(String.self) {
69+ self = .string(x)
70+ return
71+ }
72+ if let x = try? container.decode(UnionClass.self) {
73+ self = .unionClass(x)
74+ return
75+ }
76+ throw DecodingError.typeMismatch(UnionValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for UnionValue"))
77+ }
78+
79+ func encode(to encoder: Encoder) throws {
80+ var container = encoder.singleValueContainer()
81+ switch self {
82+ case .bool(let x):
83+ try container.encode(x)
84+ case .double(let x):
85+ try container.encode(x)
86+ case .string(let x):
87+ try container.encode(x)
88+ case .unionClass(let x):
89+ try container.encode(x)
90+ }
91+ }
92+}
93+
94+// MARK: - UnionClass
95+struct UnionClass: Codable {
96+ let quux: Int?
97+
98+ enum CodingKeys: String, CodingKey {
99+ case quux = "quux"
100+ }
101+}
102+
103+// MARK: UnionClass convenience initializers and mutators
104+
105+extension UnionClass {
106+ init(data: Data) throws {
107+ self = try newJSONDecoder().decode(UnionClass.self, from: data)
108+ }
109+
110+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
111+ guard let data = json.data(using: encoding) else {
112+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
113+ }
114+ try self.init(data: data)
115+ }
116+
117+ init(fromURL url: URL) throws {
118+ try self.init(data: try Data(contentsOf: url))
119+ }
120+
121+ func with(
122+ quux: Int?? = nil
123+ ) -> UnionClass {
124+ return UnionClass(
125+ quux: quux ?? self.quux
126+ )
127+ }
128+
129+ func jsonData() throws -> Data {
130+ return try newJSONEncoder().encode(self)
131+ }
132+
133+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
134+ return String(data: try self.jsonData(), encoding: encoding)
135+ }
136+}
137+
138+// MARK: - Helper functions for creating encoders and decoders
139+
140+func newJSONDecoder() -> JSONDecoder {
141+ let decoder = JSONDecoder()
142+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
143+ let container = try decoder.singleValueContainer()
144+ let dateStr = try container.decode(String.self)
145+
146+ let formatter = DateFormatter()
147+ formatter.calendar = Calendar(identifier: .iso8601)
148+ formatter.locale = Locale(identifier: "en_US_POSIX")
149+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
150+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
151+ if let date = formatter.date(from: dateStr) {
152+ return date
153+ }
154+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
155+ if let date = formatter.date(from: dateStr) {
156+ return date
157+ }
158+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
159+ })
160+ return decoder
161+}
162+
163+func newJSONEncoder() -> JSONEncoder {
164+ let encoder = JSONEncoder()
165+ let formatter = DateFormatter()
166+ formatter.calendar = Calendar(identifier: .iso8601)
167+ formatter.locale = Locale(identifier: "en_US_POSIX")
168+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
169+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
170+ encoder.dateEncodingStrategy = .formatted(formatter)
171+ return encoder
172+}
Test case

test/inputs/schema/class-with-additional.schema

1 generated file · +114 −0
Aschema-swiftdefault / quicktype.swift+114 −0
@@ -0,0 +1,114 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let map: [String: Map]?
11+
12+ enum CodingKeys: String, CodingKey {
13+ case map = "map"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ map: [String: Map]?? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ map: map ?? self.map
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+enum Map: Codable {
53+ case bool(Bool)
54+ case double(Double)
55+
56+ init(from decoder: Decoder) throws {
57+ let container = try decoder.singleValueContainer()
58+ if let x = try? container.decode(Bool.self) {
59+ self = .bool(x)
60+ return
61+ }
62+ if let x = try? container.decode(Double.self) {
63+ self = .double(x)
64+ return
65+ }
66+ throw DecodingError.typeMismatch(Map.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Map"))
67+ }
68+
69+ func encode(to encoder: Encoder) throws {
70+ var container = encoder.singleValueContainer()
71+ switch self {
72+ case .bool(let x):
73+ try container.encode(x)
74+ case .double(let x):
75+ try container.encode(x)
76+ }
77+ }
78+}
79+
80+// MARK: - Helper functions for creating encoders and decoders
81+
82+func newJSONDecoder() -> JSONDecoder {
83+ let decoder = JSONDecoder()
84+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
85+ let container = try decoder.singleValueContainer()
86+ let dateStr = try container.decode(String.self)
87+
88+ let formatter = DateFormatter()
89+ formatter.calendar = Calendar(identifier: .iso8601)
90+ formatter.locale = Locale(identifier: "en_US_POSIX")
91+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
92+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
93+ if let date = formatter.date(from: dateStr) {
94+ return date
95+ }
96+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
97+ if let date = formatter.date(from: dateStr) {
98+ return date
99+ }
100+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
101+ })
102+ return decoder
103+}
104+
105+func newJSONEncoder() -> JSONEncoder {
106+ let encoder = JSONEncoder()
107+ let formatter = DateFormatter()
108+ formatter.calendar = Calendar(identifier: .iso8601)
109+ formatter.locale = Locale(identifier: "en_US_POSIX")
110+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
111+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
112+ encoder.dateEncodingStrategy = .formatted(formatter)
113+ return encoder
114+}
Test case

test/inputs/schema/const-non-string.schema

1 generated file · +106 −0
Aschema-swiftdefault / quicktype.swift+106 −0
@@ -0,0 +1,106 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let amount: Int
11+ let enabled: Bool
12+ let kind: Kind
13+ let ratio: Double
14+ let version: Double
15+
16+ enum CodingKeys: String, CodingKey {
17+ case amount = "amount"
18+ case enabled = "enabled"
19+ case kind = "kind"
20+ case ratio = "ratio"
21+ case version = "version"
22+ }
23+}
24+
25+// MARK: TopLevel convenience initializers and mutators
26+
27+extension TopLevel {
28+ init(data: Data) throws {
29+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
30+ }
31+
32+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
33+ guard let data = json.data(using: encoding) else {
34+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
35+ }
36+ try self.init(data: data)
37+ }
38+
39+ init(fromURL url: URL) throws {
40+ try self.init(data: try Data(contentsOf: url))
41+ }
42+
43+ func with(
44+ amount: Int? = nil,
45+ enabled: Bool? = nil,
46+ kind: Kind? = nil,
47+ ratio: Double? = nil,
48+ version: Double? = nil
49+ ) -> TopLevel {
50+ return TopLevel(
51+ amount: amount ?? self.amount,
52+ enabled: enabled ?? self.enabled,
53+ kind: kind ?? self.kind,
54+ ratio: ratio ?? self.ratio,
55+ version: version ?? self.version
56+ )
57+ }
58+
59+ func jsonData() throws -> Data {
60+ return try newJSONEncoder().encode(self)
61+ }
62+
63+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
64+ return String(data: try self.jsonData(), encoding: encoding)
65+ }
66+}
67+
68+enum Kind: String, Codable {
69+ case widget = "widget"
70+}
71+
72+// MARK: - Helper functions for creating encoders and decoders
73+
74+func newJSONDecoder() -> JSONDecoder {
75+ let decoder = JSONDecoder()
76+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
77+ let container = try decoder.singleValueContainer()
78+ let dateStr = try container.decode(String.self)
79+
80+ let formatter = DateFormatter()
81+ formatter.calendar = Calendar(identifier: .iso8601)
82+ formatter.locale = Locale(identifier: "en_US_POSIX")
83+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
84+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
85+ if let date = formatter.date(from: dateStr) {
86+ return date
87+ }
88+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
89+ if let date = formatter.date(from: dateStr) {
90+ return date
91+ }
92+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
93+ })
94+ return decoder
95+}
96+
97+func newJSONEncoder() -> JSONEncoder {
98+ let encoder = JSONEncoder()
99+ let formatter = DateFormatter()
100+ formatter.calendar = Calendar(identifier: .iso8601)
101+ formatter.locale = Locale(identifier: "en_US_POSIX")
102+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
103+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
104+ encoder.dateEncodingStrategy = .formatted(formatter)
105+ return encoder
106+}
Test case

test/inputs/schema/date-time.schema

1 generated file · +142 −0
Aschema-swiftdefault / quicktype.swift+142 −0
@@ -0,0 +1,142 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let complexUnionArray: [ComplexUnionArrayElement]
11+ let date: String
12+ let dateTime: Date
13+ let time: String
14+ let unionArray: [String]
15+
16+ enum CodingKeys: String, CodingKey {
17+ case complexUnionArray = "complex-union-array"
18+ case date = "date"
19+ case dateTime = "date-time"
20+ case time = "time"
21+ case unionArray = "union-array"
22+ }
23+}
24+
25+// MARK: TopLevel convenience initializers and mutators
26+
27+extension TopLevel {
28+ init(data: Data) throws {
29+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
30+ }
31+
32+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
33+ guard let data = json.data(using: encoding) else {
34+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
35+ }
36+ try self.init(data: data)
37+ }
38+
39+ init(fromURL url: URL) throws {
40+ try self.init(data: try Data(contentsOf: url))
41+ }
42+
43+ func with(
44+ complexUnionArray: [ComplexUnionArrayElement]? = nil,
45+ date: String? = nil,
46+ dateTime: Date? = nil,
47+ time: String? = nil,
48+ unionArray: [String]? = nil
49+ ) -> TopLevel {
50+ return TopLevel(
51+ complexUnionArray: complexUnionArray ?? self.complexUnionArray,
52+ date: date ?? self.date,
53+ dateTime: dateTime ?? self.dateTime,
54+ time: time ?? self.time,
55+ unionArray: unionArray ?? self.unionArray
56+ )
57+ }
58+
59+ func jsonData() throws -> Data {
60+ return try newJSONEncoder().encode(self)
61+ }
62+
63+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
64+ return String(data: try self.jsonData(), encoding: encoding)
65+ }
66+}
67+
68+enum ComplexUnionArrayElement: Codable {
69+ case dateTime(Date)
70+ case enumeration(ComplexUnionArrayEnum)
71+ case integer(Int)
72+
73+ init(from decoder: Decoder) throws {
74+ let container = try decoder.singleValueContainer()
75+ if let x = try? container.decode(Int.self) {
76+ self = .integer(x)
77+ return
78+ }
79+ if let x = try? container.decode(Date.self) {
80+ self = .dateTime(x)
81+ return
82+ }
83+ if let x = try? container.decode(ComplexUnionArrayEnum.self) {
84+ self = .enumeration(x)
85+ return
86+ }
87+ throw DecodingError.typeMismatch(ComplexUnionArrayElement.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ComplexUnionArrayElement"))
88+ }
89+
90+ func encode(to encoder: Encoder) throws {
91+ var container = encoder.singleValueContainer()
92+ switch self {
93+ case .dateTime(let x):
94+ try container.encode(x)
95+ case .enumeration(let x):
96+ try container.encode(x)
97+ case .integer(let x):
98+ try container.encode(x)
99+ }
100+ }
101+}
102+
103+enum ComplexUnionArrayEnum: String, Codable {
104+ case foo = "foo"
105+ case bar = "bar"
106+}
107+
108+// MARK: - Helper functions for creating encoders and decoders
109+
110+func newJSONDecoder() -> JSONDecoder {
111+ let decoder = JSONDecoder()
112+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
113+ let container = try decoder.singleValueContainer()
114+ let dateStr = try container.decode(String.self)
115+
116+ let formatter = DateFormatter()
117+ formatter.calendar = Calendar(identifier: .iso8601)
118+ formatter.locale = Locale(identifier: "en_US_POSIX")
119+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
120+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
121+ if let date = formatter.date(from: dateStr) {
122+ return date
123+ }
124+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
125+ if let date = formatter.date(from: dateStr) {
126+ return date
127+ }
128+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
129+ })
130+ return decoder
131+}
132+
133+func newJSONEncoder() -> JSONEncoder {
134+ let encoder = JSONEncoder()
135+ let formatter = DateFormatter()
136+ formatter.calendar = Calendar(identifier: .iso8601)
137+ formatter.locale = Locale(identifier: "en_US_POSIX")
138+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
139+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
140+ encoder.dateEncodingStrategy = .formatted(formatter)
141+ return encoder
142+}
Test case

test/inputs/schema/default-value.schema

1 generated file · +87 −0
Aschema-swiftdefault / quicktype.swift+87 −0
@@ -0,0 +1,87 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+/// An object with a scalar default
9+// MARK: - TopLevel
10+struct TopLevel: Codable {
11+ let id: Int
12+
13+ enum CodingKeys: String, CodingKey {
14+ case id = "id"
15+ }
16+}
17+
18+// MARK: TopLevel convenience initializers and mutators
19+
20+extension TopLevel {
21+ init(data: Data) throws {
22+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
23+ }
24+
25+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
26+ guard let data = json.data(using: encoding) else {
27+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
28+ }
29+ try self.init(data: data)
30+ }
31+
32+ init(fromURL url: URL) throws {
33+ try self.init(data: try Data(contentsOf: url))
34+ }
35+
36+ func with(
37+ id: Int? = nil
38+ ) -> TopLevel {
39+ return TopLevel(
40+ id: id ?? self.id
41+ )
42+ }
43+
44+ func jsonData() throws -> Data {
45+ return try newJSONEncoder().encode(self)
46+ }
47+
48+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
49+ return String(data: try self.jsonData(), encoding: encoding)
50+ }
51+}
52+
53+// MARK: - Helper functions for creating encoders and decoders
54+
55+func newJSONDecoder() -> JSONDecoder {
56+ let decoder = JSONDecoder()
57+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
58+ let container = try decoder.singleValueContainer()
59+ let dateStr = try container.decode(String.self)
60+
61+ let formatter = DateFormatter()
62+ formatter.calendar = Calendar(identifier: .iso8601)
63+ formatter.locale = Locale(identifier: "en_US_POSIX")
64+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
65+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
66+ if let date = formatter.date(from: dateStr) {
67+ return date
68+ }
69+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
70+ if let date = formatter.date(from: dateStr) {
71+ return date
72+ }
73+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
74+ })
75+ return decoder
76+}
77+
78+func newJSONEncoder() -> JSONEncoder {
79+ let encoder = JSONEncoder()
80+ let formatter = DateFormatter()
81+ formatter.calendar = Calendar(identifier: .iso8601)
82+ formatter.locale = Locale(identifier: "en_US_POSIX")
83+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
84+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
85+ encoder.dateEncodingStrategy = .formatted(formatter)
86+ return encoder
87+}
Test case

test/inputs/schema/enum-large.schema

1 generated file · +119 −0
Aschema-swiftdefault / quicktype.swift+119 −0
@@ -0,0 +1,119 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let callsign: Callsign
11+ let priority: Priority
12+
13+ enum CodingKeys: String, CodingKey {
14+ case callsign = "callsign"
15+ case priority = "priority"
16+ }
17+}
18+
19+// MARK: TopLevel convenience initializers and mutators
20+
21+extension TopLevel {
22+ init(data: Data) throws {
23+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
24+ }
25+
26+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
27+ guard let data = json.data(using: encoding) else {
28+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
29+ }
30+ try self.init(data: data)
31+ }
32+
33+ init(fromURL url: URL) throws {
34+ try self.init(data: try Data(contentsOf: url))
35+ }
36+
37+ func with(
38+ callsign: Callsign? = nil,
39+ priority: Priority? = nil
40+ ) -> TopLevel {
41+ return TopLevel(
42+ callsign: callsign ?? self.callsign,
43+ priority: priority ?? self.priority
44+ )
45+ }
46+
47+ func jsonData() throws -> Data {
48+ return try newJSONEncoder().encode(self)
49+ }
50+
51+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
52+ return String(data: try self.jsonData(), encoding: encoding)
53+ }
54+}
55+
56+enum Callsign: String, Codable {
57+ case alpha = "alpha"
58+ case bravo = "bravo"
59+ case charlie = "charlie"
60+ case delta = "delta"
61+ case echo = "echo"
62+ case foxtrot = "foxtrot"
63+ case golf = "golf"
64+ case hotel = "hotel"
65+ case india = "india"
66+ case juliett = "juliett"
67+ case kilo = "kilo"
68+ case lima = "lima"
69+ case mike = "mike"
70+ case november = "november"
71+ case oscar = "oscar"
72+ case papa = "papa"
73+ case quebec = "quebec"
74+ case romeo = "romeo"
75+ case sierra = "sierra"
76+ case tango = "tango"
77+}
78+
79+enum Priority: String, Codable {
80+ case low = "low"
81+ case medium = "medium"
82+ case high = "high"
83+}
84+
85+// MARK: - Helper functions for creating encoders and decoders
86+
87+func newJSONDecoder() -> JSONDecoder {
88+ let decoder = JSONDecoder()
89+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
90+ let container = try decoder.singleValueContainer()
91+ let dateStr = try container.decode(String.self)
92+
93+ let formatter = DateFormatter()
94+ formatter.calendar = Calendar(identifier: .iso8601)
95+ formatter.locale = Locale(identifier: "en_US_POSIX")
96+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
97+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
98+ if let date = formatter.date(from: dateStr) {
99+ return date
100+ }
101+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
102+ if let date = formatter.date(from: dateStr) {
103+ return date
104+ }
105+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
106+ })
107+ return decoder
108+}
109+
110+func newJSONEncoder() -> JSONEncoder {
111+ let encoder = JSONEncoder()
112+ let formatter = DateFormatter()
113+ formatter.calendar = Calendar(identifier: .iso8601)
114+ formatter.locale = Locale(identifier: "en_US_POSIX")
115+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
116+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
117+ encoder.dateEncodingStrategy = .formatted(formatter)
118+ return encoder
119+}
Test case

test/inputs/schema/enum.schema

1 generated file · +148 −0
Aschema-swiftdefault / quicktype.swift+148 −0
@@ -0,0 +1,148 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let arr: [Arr]?
11+ let topLevelFor: String?
12+ let gve: Gve
13+ let lvc: Lvc?
14+ let otherArr: [OtherArr]?
15+
16+ enum CodingKeys: String, CodingKey {
17+ case arr = "arr"
18+ case topLevelFor = "for"
19+ case gve = "gve"
20+ case lvc = "lvc"
21+ case otherArr = "otherArr"
22+ }
23+}
24+
25+// MARK: TopLevel convenience initializers and mutators
26+
27+extension TopLevel {
28+ init(data: Data) throws {
29+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
30+ }
31+
32+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
33+ guard let data = json.data(using: encoding) else {
34+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
35+ }
36+ try self.init(data: data)
37+ }
38+
39+ init(fromURL url: URL) throws {
40+ try self.init(data: try Data(contentsOf: url))
41+ }
42+
43+ func with(
44+ arr: [Arr]?? = nil,
45+ topLevelFor: String?? = nil,
46+ gve: Gve? = nil,
47+ lvc: Lvc?? = nil,
48+ otherArr: [OtherArr]?? = nil
49+ ) -> TopLevel {
50+ return TopLevel(
51+ arr: arr ?? self.arr,
52+ topLevelFor: topLevelFor ?? self.topLevelFor,
53+ gve: gve ?? self.gve,
54+ lvc: lvc ?? self.lvc,
55+ otherArr: otherArr ?? self.otherArr
56+ )
57+ }
58+
59+ func jsonData() throws -> Data {
60+ return try newJSONEncoder().encode(self)
61+ }
62+
63+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
64+ return String(data: try self.jsonData(), encoding: encoding)
65+ }
66+}
67+
68+enum Arr: Codable {
69+ case enumeration(OtherArr)
70+ case integer(Int)
71+
72+ init(from decoder: Decoder) throws {
73+ let container = try decoder.singleValueContainer()
74+ if let x = try? container.decode(Int.self) {
75+ self = .integer(x)
76+ return
77+ }
78+ if let x = try? container.decode(OtherArr.self) {
79+ self = .enumeration(x)
80+ return
81+ }
82+ throw DecodingError.typeMismatch(Arr.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Arr"))
83+ }
84+
85+ func encode(to encoder: Encoder) throws {
86+ var container = encoder.singleValueContainer()
87+ switch self {
88+ case .enumeration(let x):
89+ try container.encode(x)
90+ case .integer(let x):
91+ try container.encode(x)
92+ }
93+ }
94+}
95+
96+enum OtherArr: String, Codable {
97+ case foo = "foo"
98+ case bar = "bar"
99+ case otherArrIf = "if"
100+}
101+
102+enum Gve: String, Codable {
103+ case good = "good"
104+ case neutral = "neutral"
105+ case evil = "evil"
106+}
107+
108+enum Lvc: String, Codable {
109+ case lawful = "lawful"
110+ case neutral = "neutral"
111+ case chaotic = "chaotic"
112+}
113+
114+// MARK: - Helper functions for creating encoders and decoders
115+
116+func newJSONDecoder() -> JSONDecoder {
117+ let decoder = JSONDecoder()
118+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
119+ let container = try decoder.singleValueContainer()
120+ let dateStr = try container.decode(String.self)
121+
122+ let formatter = DateFormatter()
123+ formatter.calendar = Calendar(identifier: .iso8601)
124+ formatter.locale = Locale(identifier: "en_US_POSIX")
125+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
126+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
127+ if let date = formatter.date(from: dateStr) {
128+ return date
129+ }
130+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
131+ if let date = formatter.date(from: dateStr) {
132+ return date
133+ }
134+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
135+ })
136+ return decoder
137+}
138+
139+func newJSONEncoder() -> JSONEncoder {
140+ let encoder = JSONEncoder()
141+ let formatter = DateFormatter()
142+ formatter.calendar = Calendar(identifier: .iso8601)
143+ formatter.locale = Locale(identifier: "en_US_POSIX")
144+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
145+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
146+ encoder.dateEncodingStrategy = .formatted(formatter)
147+ return encoder
148+}
Test case

test/inputs/schema/go-schema-pattern-properties.schema

1 generated file · +86 −0
Aschema-swiftdefault / quicktype.swift+86 −0
@@ -0,0 +1,86 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let map: [String: Int]
11+
12+ enum CodingKeys: String, CodingKey {
13+ case map = "map"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ map: [String: Int]? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ map: map ?? self.map
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+// MARK: - Helper functions for creating encoders and decoders
53+
54+func newJSONDecoder() -> JSONDecoder {
55+ let decoder = JSONDecoder()
56+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
57+ let container = try decoder.singleValueContainer()
58+ let dateStr = try container.decode(String.self)
59+
60+ let formatter = DateFormatter()
61+ formatter.calendar = Calendar(identifier: .iso8601)
62+ formatter.locale = Locale(identifier: "en_US_POSIX")
63+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
64+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
65+ if let date = formatter.date(from: dateStr) {
66+ return date
67+ }
68+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
69+ if let date = formatter.date(from: dateStr) {
70+ return date
71+ }
72+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
73+ })
74+ return decoder
75+}
76+
77+func newJSONEncoder() -> JSONEncoder {
78+ let encoder = JSONEncoder()
79+ let formatter = DateFormatter()
80+ formatter.calendar = Calendar(identifier: .iso8601)
81+ formatter.locale = Locale(identifier: "en_US_POSIX")
82+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
83+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
84+ encoder.dateEncodingStrategy = .formatted(formatter)
85+ return encoder
86+}
Test case

test/inputs/schema/haskell-enum-forbidden.schema

1 generated file · +91 −0
Aschema-swiftdefault / quicktype.swift+91 −0
@@ -0,0 +1,91 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let health: Health
11+
12+ enum CodingKeys: String, CodingKey {
13+ case health = "health"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ health: Health? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ health: health ?? self.health
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+enum Health: String, Codable {
53+ case ok = "ok"
54+ case error = "error"
55+}
56+
57+// MARK: - Helper functions for creating encoders and decoders
58+
59+func newJSONDecoder() -> JSONDecoder {
60+ let decoder = JSONDecoder()
61+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
62+ let container = try decoder.singleValueContainer()
63+ let dateStr = try container.decode(String.self)
64+
65+ let formatter = DateFormatter()
66+ formatter.calendar = Calendar(identifier: .iso8601)
67+ formatter.locale = Locale(identifier: "en_US_POSIX")
68+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
69+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
70+ if let date = formatter.date(from: dateStr) {
71+ return date
72+ }
73+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
74+ if let date = formatter.date(from: dateStr) {
75+ return date
76+ }
77+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
78+ })
79+ return decoder
80+}
81+
82+func newJSONEncoder() -> JSONEncoder {
83+ let encoder = JSONEncoder()
84+ let formatter = DateFormatter()
85+ formatter.calendar = Calendar(identifier: .iso8601)
86+ formatter.locale = Locale(identifier: "en_US_POSIX")
87+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
88+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
89+ encoder.dateEncodingStrategy = .formatted(formatter)
90+ return encoder
91+}
Test case

test/inputs/schema/implicit-class-array-union.schema

1 generated file · +193 −0
Aschema-swiftdefault / quicktype.swift+193 −0
@@ -0,0 +1,193 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let foo: FooUnion
11+
12+ enum CodingKeys: String, CodingKey {
13+ case foo = "foo"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ foo: FooUnion? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ foo: foo ?? self.foo
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+enum FooUnion: Codable {
53+ case bool(Bool)
54+ case double(Double)
55+ case fooClass(FooClass)
56+ case integer(Int)
57+ case integerArray([Int])
58+ case string(String)
59+ case null
60+
61+ init(from decoder: Decoder) throws {
62+ let container = try decoder.singleValueContainer()
63+ if let x = try? container.decode(Bool.self) {
64+ self = .bool(x)
65+ return
66+ }
67+ if let x = try? container.decode(Int.self) {
68+ self = .integer(x)
69+ return
70+ }
71+ if let x = try? container.decode([Int].self) {
72+ self = .integerArray(x)
73+ return
74+ }
75+ if let x = try? container.decode(Double.self) {
76+ self = .double(x)
77+ return
78+ }
79+ if let x = try? container.decode(String.self) {
80+ self = .string(x)
81+ return
82+ }
83+ if let x = try? container.decode(FooClass.self) {
84+ self = .fooClass(x)
85+ return
86+ }
87+ if container.decodeNil() {
88+ self = .null
89+ return
90+ }
91+ throw DecodingError.typeMismatch(FooUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for FooUnion"))
92+ }
93+
94+ func encode(to encoder: Encoder) throws {
95+ var container = encoder.singleValueContainer()
96+ switch self {
97+ case .bool(let x):
98+ try container.encode(x)
99+ case .double(let x):
100+ try container.encode(x)
101+ case .fooClass(let x):
102+ try container.encode(x)
103+ case .integer(let x):
104+ try container.encode(x)
105+ case .integerArray(let x):
106+ try container.encode(x)
107+ case .string(let x):
108+ try container.encode(x)
109+ case .null:
110+ try container.encodeNil()
111+ }
112+ }
113+}
114+
115+// MARK: - FooClass
116+struct FooClass: Codable {
117+ let bar: Int
118+
119+ enum CodingKeys: String, CodingKey {
120+ case bar = "bar"
121+ }
122+}
123+
124+// MARK: FooClass convenience initializers and mutators
125+
126+extension FooClass {
127+ init(data: Data) throws {
128+ self = try newJSONDecoder().decode(FooClass.self, from: data)
129+ }
130+
131+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
132+ guard let data = json.data(using: encoding) else {
133+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
134+ }
135+ try self.init(data: data)
136+ }
137+
138+ init(fromURL url: URL) throws {
139+ try self.init(data: try Data(contentsOf: url))
140+ }
141+
142+ func with(
143+ bar: Int? = nil
144+ ) -> FooClass {
145+ return FooClass(
146+ bar: bar ?? self.bar
147+ )
148+ }
149+
150+ func jsonData() throws -> Data {
151+ return try newJSONEncoder().encode(self)
152+ }
153+
154+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
155+ return String(data: try self.jsonData(), encoding: encoding)
156+ }
157+}
158+
159+// MARK: - Helper functions for creating encoders and decoders
160+
161+func newJSONDecoder() -> JSONDecoder {
162+ let decoder = JSONDecoder()
163+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
164+ let container = try decoder.singleValueContainer()
165+ let dateStr = try container.decode(String.self)
166+
167+ let formatter = DateFormatter()
168+ formatter.calendar = Calendar(identifier: .iso8601)
169+ formatter.locale = Locale(identifier: "en_US_POSIX")
170+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
171+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
172+ if let date = formatter.date(from: dateStr) {
173+ return date
174+ }
175+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
176+ if let date = formatter.date(from: dateStr) {
177+ return date
178+ }
179+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
180+ })
181+ return decoder
182+}
183+
184+func newJSONEncoder() -> JSONEncoder {
185+ let encoder = JSONEncoder()
186+ let formatter = DateFormatter()
187+ formatter.calendar = Calendar(identifier: .iso8601)
188+ formatter.locale = Locale(identifier: "en_US_POSIX")
189+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
190+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
191+ encoder.dateEncodingStrategy = .formatted(formatter)
192+ return encoder
193+}
Test case

test/inputs/schema/intersection.schema

1 generated file · +134 −0
Aschema-swiftdefault / quicktype.swift+134 −0
@@ -0,0 +1,134 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let intersection: Intersection?
11+
12+ enum CodingKeys: String, CodingKey {
13+ case intersection = "intersection"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ intersection: Intersection?? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ intersection: intersection ?? self.intersection
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+// MARK: - Intersection
53+struct Intersection: Codable {
54+ let foo: Double
55+ let bar: String?
56+
57+ enum CodingKeys: String, CodingKey {
58+ case foo = "foo"
59+ case bar = "bar"
60+ }
61+}
62+
63+// MARK: Intersection convenience initializers and mutators
64+
65+extension Intersection {
66+ init(data: Data) throws {
67+ self = try newJSONDecoder().decode(Intersection.self, from: data)
68+ }
69+
70+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
71+ guard let data = json.data(using: encoding) else {
72+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
73+ }
74+ try self.init(data: data)
75+ }
76+
77+ init(fromURL url: URL) throws {
78+ try self.init(data: try Data(contentsOf: url))
79+ }
80+
81+ func with(
82+ foo: Double? = nil,
83+ bar: String?? = nil
84+ ) -> Intersection {
85+ return Intersection(
86+ foo: foo ?? self.foo,
87+ bar: bar ?? self.bar
88+ )
89+ }
90+
91+ func jsonData() throws -> Data {
92+ return try newJSONEncoder().encode(self)
93+ }
94+
95+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
96+ return String(data: try self.jsonData(), encoding: encoding)
97+ }
98+}
99+
100+// MARK: - Helper functions for creating encoders and decoders
101+
102+func newJSONDecoder() -> JSONDecoder {
103+ let decoder = JSONDecoder()
104+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
105+ let container = try decoder.singleValueContainer()
106+ let dateStr = try container.decode(String.self)
107+
108+ let formatter = DateFormatter()
109+ formatter.calendar = Calendar(identifier: .iso8601)
110+ formatter.locale = Locale(identifier: "en_US_POSIX")
111+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
112+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
113+ if let date = formatter.date(from: dateStr) {
114+ return date
115+ }
116+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
117+ if let date = formatter.date(from: dateStr) {
118+ return date
119+ }
120+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
121+ })
122+ return decoder
123+}
124+
125+func newJSONEncoder() -> JSONEncoder {
126+ let encoder = JSONEncoder()
127+ let formatter = DateFormatter()
128+ formatter.calendar = Calendar(identifier: .iso8601)
129+ formatter.locale = Locale(identifier: "en_US_POSIX")
130+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
131+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
132+ encoder.dateEncodingStrategy = .formatted(formatter)
133+ return encoder
134+}
Test case

test/inputs/schema/multi-type-enum.schema

1 generated file · +134 −0
Aschema-swiftdefault / quicktype.swift+134 −0
@@ -0,0 +1,134 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let foo: FooUnion
11+
12+ enum CodingKeys: String, CodingKey {
13+ case foo = "foo"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ foo: FooUnion? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ foo: foo ?? self.foo
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+enum FooUnion: Codable {
53+ case bool(Bool)
54+ case double(Double)
55+ case enumeration(FooEnum)
56+ case integer(Int)
57+
58+ init(from decoder: Decoder) throws {
59+ let container = try decoder.singleValueContainer()
60+ if let x = try? container.decode(Bool.self) {
61+ self = .bool(x)
62+ return
63+ }
64+ if let x = try? container.decode(Int.self) {
65+ self = .integer(x)
66+ return
67+ }
68+ if let x = try? container.decode(Double.self) {
69+ self = .double(x)
70+ return
71+ }
72+ if let x = try? container.decode(FooEnum.self) {
73+ self = .enumeration(x)
74+ return
75+ }
76+ throw DecodingError.typeMismatch(FooUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for FooUnion"))
77+ }
78+
79+ func encode(to encoder: Encoder) throws {
80+ var container = encoder.singleValueContainer()
81+ switch self {
82+ case .bool(let x):
83+ try container.encode(x)
84+ case .double(let x):
85+ try container.encode(x)
86+ case .enumeration(let x):
87+ try container.encode(x)
88+ case .integer(let x):
89+ try container.encode(x)
90+ }
91+ }
92+}
93+
94+enum FooEnum: String, Codable {
95+ case a = "a"
96+ case b = "b"
97+ case c = "c"
98+}
99+
100+// MARK: - Helper functions for creating encoders and decoders
101+
102+func newJSONDecoder() -> JSONDecoder {
103+ let decoder = JSONDecoder()
104+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
105+ let container = try decoder.singleValueContainer()
106+ let dateStr = try container.decode(String.self)
107+
108+ let formatter = DateFormatter()
109+ formatter.calendar = Calendar(identifier: .iso8601)
110+ formatter.locale = Locale(identifier: "en_US_POSIX")
111+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
112+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
113+ if let date = formatter.date(from: dateStr) {
114+ return date
115+ }
116+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
117+ if let date = formatter.date(from: dateStr) {
118+ return date
119+ }
120+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
121+ })
122+ return decoder
123+}
124+
125+func newJSONEncoder() -> JSONEncoder {
126+ let encoder = JSONEncoder()
127+ let formatter = DateFormatter()
128+ formatter.calendar = Calendar(identifier: .iso8601)
129+ formatter.locale = Locale(identifier: "en_US_POSIX")
130+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
131+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
132+ encoder.dateEncodingStrategy = .formatted(formatter)
133+ return encoder
134+}
Test case

test/inputs/schema/nullable-optional-one-of.schema

1 generated file · +95 −0
Aschema-swiftdefault / quicktype.swift+95 −0
@@ -0,0 +1,95 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let b: String?
11+ let kind: Kind
12+
13+ enum CodingKeys: String, CodingKey {
14+ case b = "b"
15+ case kind = "kind"
16+ }
17+}
18+
19+// MARK: TopLevel convenience initializers and mutators
20+
21+extension TopLevel {
22+ init(data: Data) throws {
23+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
24+ }
25+
26+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
27+ guard let data = json.data(using: encoding) else {
28+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
29+ }
30+ try self.init(data: data)
31+ }
32+
33+ init(fromURL url: URL) throws {
34+ try self.init(data: try Data(contentsOf: url))
35+ }
36+
37+ func with(
38+ b: String?? = nil,
39+ kind: Kind? = nil
40+ ) -> TopLevel {
41+ return TopLevel(
42+ b: b ?? self.b,
43+ kind: kind ?? self.kind
44+ )
45+ }
46+
47+ func jsonData() throws -> Data {
48+ return try newJSONEncoder().encode(self)
49+ }
50+
51+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
52+ return String(data: try self.jsonData(), encoding: encoding)
53+ }
54+}
55+
56+enum Kind: String, Codable {
57+ case one = "one"
58+ case two = "two"
59+}
60+
61+// MARK: - Helper functions for creating encoders and decoders
62+
63+func newJSONDecoder() -> JSONDecoder {
64+ let decoder = JSONDecoder()
65+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
66+ let container = try decoder.singleValueContainer()
67+ let dateStr = try container.decode(String.self)
68+
69+ let formatter = DateFormatter()
70+ formatter.calendar = Calendar(identifier: .iso8601)
71+ formatter.locale = Locale(identifier: "en_US_POSIX")
72+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
73+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
74+ if let date = formatter.date(from: dateStr) {
75+ return date
76+ }
77+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
78+ if let date = formatter.date(from: dateStr) {
79+ return date
80+ }
81+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
82+ })
83+ return decoder
84+}
85+
86+func newJSONEncoder() -> JSONEncoder {
87+ let encoder = JSONEncoder()
88+ let formatter = DateFormatter()
89+ formatter.calendar = Calendar(identifier: .iso8601)
90+ formatter.locale = Locale(identifier: "en_US_POSIX")
91+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
92+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
93+ encoder.dateEncodingStrategy = .formatted(formatter)
94+ return encoder
95+}
Test case

test/inputs/schema/required.schema

1 generated file · +86 −0
Aschema-swiftdefault / quicktype.swift+86 −0
@@ -0,0 +1,86 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let longitude: Double
11+
12+ enum CodingKeys: String, CodingKey {
13+ case longitude = "longitude"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ longitude: Double? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ longitude: longitude ?? self.longitude
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+// MARK: - Helper functions for creating encoders and decoders
53+
54+func newJSONDecoder() -> JSONDecoder {
55+ let decoder = JSONDecoder()
56+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
57+ let container = try decoder.singleValueContainer()
58+ let dateStr = try container.decode(String.self)
59+
60+ let formatter = DateFormatter()
61+ formatter.calendar = Calendar(identifier: .iso8601)
62+ formatter.locale = Locale(identifier: "en_US_POSIX")
63+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
64+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
65+ if let date = formatter.date(from: dateStr) {
66+ return date
67+ }
68+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
69+ if let date = formatter.date(from: dateStr) {
70+ return date
71+ }
72+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
73+ })
74+ return decoder
75+}
76+
77+func newJSONEncoder() -> JSONEncoder {
78+ let encoder = JSONEncoder()
79+ let formatter = DateFormatter()
80+ formatter.calendar = Calendar(identifier: .iso8601)
81+ formatter.locale = Locale(identifier: "en_US_POSIX")
82+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
83+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
84+ encoder.dateEncodingStrategy = .formatted(formatter)
85+ return encoder
86+}
Test case

test/inputs/schema/unevaluated-properties.schema

1 generated file · +528 −0
Aschema-swiftdefault / quicktype.swift+528 −0
@@ -0,0 +1,528 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ let config: Config?
11+
12+ enum CodingKeys: String, CodingKey {
13+ case config = "config"
14+ }
15+}
16+
17+// MARK: TopLevel convenience initializers and mutators
18+
19+extension TopLevel {
20+ init(data: Data) throws {
21+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
22+ }
23+
24+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
25+ guard let data = json.data(using: encoding) else {
26+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
27+ }
28+ try self.init(data: data)
29+ }
30+
31+ init(fromURL url: URL) throws {
32+ try self.init(data: try Data(contentsOf: url))
33+ }
34+
35+ func with(
36+ config: Config?? = nil
37+ ) -> TopLevel {
38+ return TopLevel(
39+ config: config ?? self.config
40+ )
41+ }
42+
43+ func jsonData() throws -> Data {
44+ return try newJSONEncoder().encode(self)
45+ }
46+
47+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
48+ return String(data: try self.jsonData(), encoding: encoding)
49+ }
50+}
51+
52+// MARK: - Config
53+struct Config: Codable {
54+ let closed: Closed?
55+ let name: String?
56+ let settings: [String: [Item]]?
57+
58+ enum CodingKeys: String, CodingKey {
59+ case closed = "closed"
60+ case name = "name"
61+ case settings = "settings"
62+ }
63+}
64+
65+// MARK: Config convenience initializers and mutators
66+
67+extension Config {
68+ init(data: Data) throws {
69+ self = try newJSONDecoder().decode(Config.self, from: data)
70+ }
71+
72+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
73+ guard let data = json.data(using: encoding) else {
74+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
75+ }
76+ try self.init(data: data)
77+ }
78+
79+ init(fromURL url: URL) throws {
80+ try self.init(data: try Data(contentsOf: url))
81+ }
82+
83+ func with(
84+ closed: Closed?? = nil,
85+ name: String?? = nil,
86+ settings: [String: [Item]]?? = nil
87+ ) -> Config {
88+ return Config(
89+ closed: closed ?? self.closed,
90+ name: name ?? self.name,
91+ settings: settings ?? self.settings
92+ )
93+ }
94+
95+ func jsonData() throws -> Data {
96+ return try newJSONEncoder().encode(self)
97+ }
98+
99+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
100+ return String(data: try self.jsonData(), encoding: encoding)
101+ }
102+}
103+
104+enum Closed: Codable {
105+ case anythingArray([JSONAny])
106+ case bool(Bool)
107+ case closedClass(ClosedClass)
108+ case double(Double)
109+ case integer(Int)
110+ case string(String)
111+ case null
112+
113+ init(from decoder: Decoder) throws {
114+ let container = try decoder.singleValueContainer()
115+ if let x = try? container.decode(Bool.self) {
116+ self = .bool(x)
117+ return
118+ }
119+ if let x = try? container.decode(Int.self) {
120+ self = .integer(x)
121+ return
122+ }
123+ if let x = try? container.decode([JSONAny].self) {
124+ self = .anythingArray(x)
125+ return
126+ }
127+ if let x = try? container.decode(Double.self) {
128+ self = .double(x)
129+ return
130+ }
131+ if let x = try? container.decode(String.self) {
132+ self = .string(x)
133+ return
134+ }
135+ if let x = try? container.decode(ClosedClass.self) {
136+ self = .closedClass(x)
137+ return
138+ }
139+ if container.decodeNil() {
140+ self = .null
141+ return
142+ }
143+ throw DecodingError.typeMismatch(Closed.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Closed"))
144+ }
145+
146+ func encode(to encoder: Encoder) throws {
147+ var container = encoder.singleValueContainer()
148+ switch self {
149+ case .anythingArray(let x):
150+ try container.encode(x)
151+ case .bool(let x):
152+ try container.encode(x)
153+ case .closedClass(let x):
154+ try container.encode(x)
155+ case .double(let x):
156+ try container.encode(x)
157+ case .integer(let x):
158+ try container.encode(x)
159+ case .string(let x):
160+ try container.encode(x)
161+ case .null:
162+ try container.encodeNil()
163+ }
164+ }
165+}
166+
167+// MARK: - ClosedClass
168+struct ClosedClass: Codable {
169+}
170+
171+// MARK: ClosedClass convenience initializers and mutators
172+
173+extension ClosedClass {
174+ init(data: Data) throws {
175+ self = try newJSONDecoder().decode(ClosedClass.self, from: data)
176+ }
177+
178+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
179+ guard let data = json.data(using: encoding) else {
180+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
181+ }
182+ try self.init(data: data)
183+ }
184+
185+ init(fromURL url: URL) throws {
186+ try self.init(data: try Data(contentsOf: url))
187+ }
188+
189+ func with(
190+ ) -> ClosedClass {
191+ return ClosedClass(
192+ )
193+ }
194+
195+ func jsonData() throws -> Data {
196+ return try newJSONEncoder().encode(self)
197+ }
198+
199+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
200+ return String(data: try self.jsonData(), encoding: encoding)
201+ }
202+}
203+
204+// MARK: - Item
205+struct Item: Codable {
206+ let key: String
207+ let value: String
208+
209+ enum CodingKeys: String, CodingKey {
210+ case key = "key"
211+ case value = "value"
212+ }
213+}
214+
215+// MARK: Item convenience initializers and mutators
216+
217+extension Item {
218+ init(data: Data) throws {
219+ self = try newJSONDecoder().decode(Item.self, from: data)
220+ }
221+
222+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
223+ guard let data = json.data(using: encoding) else {
224+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
225+ }
226+ try self.init(data: data)
227+ }
228+
229+ init(fromURL url: URL) throws {
230+ try self.init(data: try Data(contentsOf: url))
231+ }
232+
233+ func with(
234+ key: String? = nil,
235+ value: String? = nil
236+ ) -> Item {
237+ return Item(
238+ key: key ?? self.key,
239+ value: value ?? self.value
240+ )
241+ }
242+
243+ func jsonData() throws -> Data {
244+ return try newJSONEncoder().encode(self)
245+ }
246+
247+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
248+ return String(data: try self.jsonData(), encoding: encoding)
249+ }
250+}
251+
252+// MARK: - Helper functions for creating encoders and decoders
253+
254+func newJSONDecoder() -> JSONDecoder {
255+ let decoder = JSONDecoder()
256+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
257+ let container = try decoder.singleValueContainer()
258+ let dateStr = try container.decode(String.self)
259+
260+ let formatter = DateFormatter()
261+ formatter.calendar = Calendar(identifier: .iso8601)
262+ formatter.locale = Locale(identifier: "en_US_POSIX")
263+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
264+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
265+ if let date = formatter.date(from: dateStr) {
266+ return date
267+ }
268+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
269+ if let date = formatter.date(from: dateStr) {
270+ return date
271+ }
272+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
273+ })
274+ return decoder
275+}
276+
277+func newJSONEncoder() -> JSONEncoder {
278+ let encoder = JSONEncoder()
279+ let formatter = DateFormatter()
280+ formatter.calendar = Calendar(identifier: .iso8601)
281+ formatter.locale = Locale(identifier: "en_US_POSIX")
282+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
283+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
284+ encoder.dateEncodingStrategy = .formatted(formatter)
285+ return encoder
286+}
287+
288+// MARK: - Encode/decode helpers
289+
290+class JSONNull: Codable, Hashable {
291+
292+ public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
293+ return true
294+ }
295+
296+ public func hash(into hasher: inout Hasher) {
297+ hasher.combine(0)
298+ }
299+
300+ public init() {}
301+
302+ public required init(from decoder: Decoder) throws {
303+ let container = try decoder.singleValueContainer()
304+ if !container.decodeNil() {
305+ throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
306+ }
307+ }
308+
309+ public func encode(to encoder: Encoder) throws {
310+ var container = encoder.singleValueContainer()
311+ try container.encodeNil()
312+ }
313+}
314+
315+class JSONCodingKey: CodingKey {
316+ let key: String
317+
318+ required init?(intValue: Int) {
319+ return nil
320+ }
321+
322+ required init?(stringValue: String) {
323+ key = stringValue
324+ }
325+
326+ var intValue: Int? {
327+ return nil
328+ }
329+
330+ var stringValue: String {
331+ return key
332+ }
333+}
334+
335+class JSONAny: Codable {
336+
337+ let value: Any
338+
339+ static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
340+ let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
341+ return DecodingError.typeMismatch(JSONAny.self, context)
342+ }
343+
344+ static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
345+ let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
346+ return EncodingError.invalidValue(value, context)
347+ }
348+
349+ static func decode(from container: SingleValueDecodingContainer) throws -> Any {
350+ if let value = try? container.decode(Bool.self) {
351+ return value
352+ }
353+ if let value = try? container.decode(Int64.self) {
354+ return value
355+ }
356+ if let value = try? container.decode(Double.self) {
357+ return value
358+ }
359+ if let value = try? container.decode(String.self) {
360+ return value
361+ }
362+ if container.decodeNil() {
363+ return JSONNull()
364+ }
365+ throw decodingError(forCodingPath: container.codingPath)
366+ }
367+
368+ static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
369+ if let value = try? container.decode(Bool.self) {
370+ return value
371+ }
372+ if let value = try? container.decode(Int64.self) {
373+ return value
374+ }
375+ if let value = try? container.decode(Double.self) {
376+ return value
377+ }
378+ if let value = try? container.decode(String.self) {
379+ return value
380+ }
381+ if let value = try? container.decodeNil() {
382+ if value {
383+ return JSONNull()
384+ }
385+ }
386+ if var container = try? container.nestedUnkeyedContainer() {
387+ return try decodeArray(from: &container)
388+ }
389+ if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
390+ return try decodeDictionary(from: &container)
391+ }
392+ throw decodingError(forCodingPath: container.codingPath)
393+ }
394+
395+ static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
396+ if let value = try? container.decode(Bool.self, forKey: key) {
397+ return value
398+ }
399+ if let value = try? container.decode(Int64.self, forKey: key) {
400+ return value
401+ }
402+ if let value = try? container.decode(Double.self, forKey: key) {
403+ return value
404+ }
405+ if let value = try? container.decode(String.self, forKey: key) {
406+ return value
407+ }
408+ if let value = try? container.decodeNil(forKey: key) {
409+ if value {
410+ return JSONNull()
411+ }
412+ }
413+ if var container = try? container.nestedUnkeyedContainer(forKey: key) {
414+ return try decodeArray(from: &container)
415+ }
416+ if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
417+ return try decodeDictionary(from: &container)
418+ }
419+ throw decodingError(forCodingPath: container.codingPath)
420+ }
421+
422+ static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
423+ var arr: [Any] = []
424+ while !container.isAtEnd {
425+ let value = try decode(from: &container)
426+ arr.append(value)
427+ }
428+ return arr
429+ }
430+
431+ static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
432+ var dict = [String: Any]()
433+ for key in container.allKeys {
434+ let value = try decode(from: &container, forKey: key)
435+ dict[key.stringValue] = value
436+ }
437+ return dict
438+ }
439+
440+ static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
441+ for value in array {
442+ if let value = value as? Bool {
443+ try container.encode(value)
444+ } else if let value = value as? Int64 {
445+ try container.encode(value)
446+ } else if let value = value as? Double {
447+ try container.encode(value)
448+ } else if let value = value as? String {
449+ try container.encode(value)
450+ } else if value is JSONNull {
451+ try container.encodeNil()
452+ } else if let value = value as? [Any] {
453+ var container = container.nestedUnkeyedContainer()
454+ try encode(to: &container, array: value)
455+ } else if let value = value as? [String: Any] {
456+ var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
457+ try encode(to: &container, dictionary: value)
458+ } else {
459+ throw encodingError(forValue: value, codingPath: container.codingPath)
460+ }
461+ }
462+ }
463+
464+ static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
465+ for (key, value) in dictionary {
466+ let key = JSONCodingKey(stringValue: key)!
467+ if let value = value as? Bool {
468+ try container.encode(value, forKey: key)
469+ } else if let value = value as? Int64 {
470+ try container.encode(value, forKey: key)
471+ } else if let value = value as? Double {
472+ try container.encode(value, forKey: key)
473+ } else if let value = value as? String {
474+ try container.encode(value, forKey: key)
475+ } else if value is JSONNull {
476+ try container.encodeNil(forKey: key)
477+ } else if let value = value as? [Any] {
478+ var container = container.nestedUnkeyedContainer(forKey: key)
479+ try encode(to: &container, array: value)
480+ } else if let value = value as? [String: Any] {
481+ var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
482+ try encode(to: &container, dictionary: value)
483+ } else {
484+ throw encodingError(forValue: value, codingPath: container.codingPath)
485+ }
486+ }
487+ }
488+
489+ static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
490+ if let value = value as? Bool {
491+ try container.encode(value)
492+ } else if let value = value as? Int64 {
493+ try container.encode(value)
494+ } else if let value = value as? Double {
495+ try container.encode(value)
496+ } else if let value = value as? String {
497+ try container.encode(value)
498+ } else if value is JSONNull {
499+ try container.encodeNil()
500+ } else {
501+ throw encodingError(forValue: value, codingPath: container.codingPath)
502+ }
503+ }
504+
505+ public required init(from decoder: Decoder) throws {
506+ if var arrayContainer = try? decoder.unkeyedContainer() {
507+ self.value = try JSONAny.decodeArray(from: &arrayContainer)
508+ } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
509+ self.value = try JSONAny.decodeDictionary(from: &container)
510+ } else {
511+ let container = try decoder.singleValueContainer()
512+ self.value = try JSONAny.decode(from: container)
513+ }
514+ }
515+
516+ public func encode(to encoder: Encoder) throws {
517+ if let arr = self.value as? [Any] {
518+ var container = encoder.unkeyedContainer()
519+ try JSONAny.encode(to: &container, array: arr)
520+ } else if let dict = self.value as? [String: Any] {
521+ var container = encoder.container(keyedBy: JSONCodingKey.self)
522+ try JSONAny.encode(to: &container, dictionary: dict)
523+ } else {
524+ var container = encoder.singleValueContainer()
525+ try JSONAny.encode(to: &container, value: self.value)
526+ }
527+ }
528+}
Test case

test/inputs/schema/vega-lite.schema

1 generated file · +10,653 −0
Aschema-swiftdefault / quicktype.swift+10,653 −0
@@ -0,0 +1,10653 @@
1+// This file was generated from JSON Schema using quicktype, do not modify it directly.
2+// To parse the JSON, add this file to your project and do:
3+//
4+// let topLevel = try TopLevel(json)
5+
6+import Foundation
7+
8+// MARK: - TopLevel
9+struct TopLevel: Codable {
10+ /// URL to [JSON schema](http://json-schema.org/) for a Vega-Lite specification. Unless you
11+ /// have a reason to change this, use `https://vega.github.io/schema/vega-lite/v2.json`.
12+ /// Setting the `$schema` property allows automatic validation and autocomplete in editors
13+ /// that support JSON schema.
14+ let schema: String?
15+ /// Sets how the visualization size should be determined. If a string, should be one of
16+ /// `"pad"`, `"fit"` or `"none"`.
17+ /// Object values can additionally specify parameters for content sizing and automatic
18+ /// resizing.
19+ /// `"fit"` is only supported for single and layered views that don't use `rangeStep`.
20+ ///
21+ /// __Default value__: `pad`
22+ let autosize: Autosize?
23+ /// CSS color property to use as the background of visualization.
24+ ///
25+ /// __Default value:__ none (transparent)
26+ let background: String?
27+ /// Vega-Lite configuration object. This property can only be defined at the top-level of a
28+ /// specification.
29+ let config: Config?
30+ /// An object describing the data source
31+ let data: DataClass?
32+ /// Description of this mark for commenting purpose.
33+ let description: String?
34+ /// A key-value mapping between encoding channels and definition of fields.
35+ let encoding: EncodingWithFacet?
36+ /// The height of a visualization.
37+ ///
38+ /// __Default value:__
39+ /// - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its y-channel has a
40+ /// [continuous scale](scale.html#continuous), the height will be the value of
41+ /// [`config.view.height`](spec.html#config).
42+ /// - For y-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
43+ /// value or unspecified, the height is [determined by the range step, paddings, and the
44+ /// cardinality of the field mapped to y-channel](scale.html#band). Otherwise, if the
45+ /// `rangeStep` is `null`, the height will be the value of
46+ /// [`config.view.height`](spec.html#config).
47+ /// - If no field is mapped to `y` channel, the `height` will be the value of `rangeStep`.
48+ ///
49+ /// __Note__: For plots with [`row` and `column` channels](encoding.html#facet), this
50+ /// represents the height of a single view.
51+ ///
52+ /// __See also:__ The documentation for [width and height](size.html) contains more examples.
53+ let height: Double?
54+ /// A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
55+ /// `"line"`,
56+ /// * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
57+ /// object](mark.html#mark-def).
58+ let mark: AnyMark?
59+ /// Name of the visualization for later reference.
60+ let name: String?
61+ /// The default visualization padding, in pixels, from the edge of the visualization canvas
62+ /// to the data rectangle. If a number, specifies padding for all sides.
63+ /// If an object, the value should have the format `{"left": 5, "top": 5, "right": 5,
64+ /// "bottom": 5}` to specify padding for each side of the visualization.
65+ ///
66+ /// __Default value__: `5`
67+ let padding: Padding?
68+ /// An object defining properties of geographic projection.
69+ ///
70+ /// Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
71+ /// more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
72+ let projection: Projection?
73+ /// A key-value mapping between selection names and definitions.
74+ let selection: [String: SelectionDef]?
75+ /// Title for the plot.
76+ let title: Title?
77+ /// An array of data transformations such as filter and new field calculation.
78+ let transform: [Transform]?
79+ /// The width of a visualization.
80+ ///
81+ /// __Default value:__ This will be determined by the following rules:
82+ ///
83+ /// - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its x-channel has a
84+ /// [continuous scale](scale.html#continuous), the width will be the value of
85+ /// [`config.view.width`](spec.html#config).
86+ /// - For x-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
87+ /// value or unspecified, the width is [determined by the range step, paddings, and the
88+ /// cardinality of the field mapped to x-channel](scale.html#band). Otherwise, if the
89+ /// `rangeStep` is `null`, the width will be the value of
90+ /// [`config.view.width`](spec.html#config).
91+ /// - If no field is mapped to `x` channel, the `width` will be the value of
92+ /// [`config.scale.textXRangeStep`](size.html#default-width-and-height) for `text` mark and
93+ /// the value of `rangeStep` for other marks.
94+ ///
95+ /// __Note:__ For plots with [`row` and `column` channels](encoding.html#facet), this
96+ /// represents the width of a single view.
97+ ///
98+ /// __See also:__ The documentation for [width and height](size.html) contains more examples.
99+ let width: Double?
100+ /// Layer or single view specifications to be layered.
101+ ///
102+ /// __Note__: Specifications inside `layer` cannot use `row` and `column` channels as
103+ /// layering facet specifications is not allowed.
104+ let layer: [LayerSpec]?
105+ /// Scale, axis, and legend resolutions for layers.
106+ ///
107+ /// Scale, axis, and legend resolutions for facets.
108+ ///
109+ /// Scale and legend resolutions for repeated charts.
110+ ///
111+ /// Scale, axis, and legend resolutions for vertically concatenated charts.
112+ ///
113+ /// Scale, axis, and legend resolutions for horizontally concatenated charts.
114+ let resolve: Resolve?
115+ /// An object that describes mappings between `row` and `column` channels and their field
116+ /// definitions.
117+ let facet: FacetMapping?
118+ /// A specification of the view that gets faceted.
119+ let spec: Spec?
120+ /// An object that describes what fields should be repeated into views that are laid out as a
121+ /// `row` or `column`.
122+ let topLevelRepeat: Repeat?
123+ /// A list of views that should be concatenated and put into a column.
124+ let vconcat: [Spec]?
125+ /// A list of views that should be concatenated and put into a row.
126+ let hconcat: [Spec]?
127+
128+ enum CodingKeys: String, CodingKey {
129+ case schema = "$schema"
130+ case autosize = "autosize"
131+ case background = "background"
132+ case config = "config"
133+ case data = "data"
134+ case description = "description"
135+ case encoding = "encoding"
136+ case height = "height"
137+ case mark = "mark"
138+ case name = "name"
139+ case padding = "padding"
140+ case projection = "projection"
141+ case selection = "selection"
142+ case title = "title"
143+ case transform = "transform"
144+ case width = "width"
145+ case layer = "layer"
146+ case resolve = "resolve"
147+ case facet = "facet"
148+ case spec = "spec"
149+ case topLevelRepeat = "repeat"
150+ case vconcat = "vconcat"
151+ case hconcat = "hconcat"
152+ }
153+}
154+
155+// MARK: TopLevel convenience initializers and mutators
156+
157+extension TopLevel {
158+ init(data: Data) throws {
159+ self = try newJSONDecoder().decode(TopLevel.self, from: data)
160+ }
161+
162+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
163+ guard let data = json.data(using: encoding) else {
164+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
165+ }
166+ try self.init(data: data)
167+ }
168+
169+ init(fromURL url: URL) throws {
170+ try self.init(data: try Data(contentsOf: url))
171+ }
172+
173+ func with(
174+ schema: String?? = nil,
175+ autosize: Autosize?? = nil,
176+ background: String?? = nil,
177+ config: Config?? = nil,
178+ data: DataClass?? = nil,
179+ description: String?? = nil,
180+ encoding: EncodingWithFacet?? = nil,
181+ height: Double?? = nil,
182+ mark: AnyMark?? = nil,
183+ name: String?? = nil,
184+ padding: Padding?? = nil,
185+ projection: Projection?? = nil,
186+ selection: [String: SelectionDef]?? = nil,
187+ title: Title?? = nil,
188+ transform: [Transform]?? = nil,
189+ width: Double?? = nil,
190+ layer: [LayerSpec]?? = nil,
191+ resolve: Resolve?? = nil,
192+ facet: FacetMapping?? = nil,
193+ spec: Spec?? = nil,
194+ topLevelRepeat: Repeat?? = nil,
195+ vconcat: [Spec]?? = nil,
196+ hconcat: [Spec]?? = nil
197+ ) -> TopLevel {
198+ return TopLevel(
199+ schema: schema ?? self.schema,
200+ autosize: autosize ?? self.autosize,
201+ background: background ?? self.background,
202+ config: config ?? self.config,
203+ data: data ?? self.data,
204+ description: description ?? self.description,
205+ encoding: encoding ?? self.encoding,
206+ height: height ?? self.height,
207+ mark: mark ?? self.mark,
208+ name: name ?? self.name,
209+ padding: padding ?? self.padding,
210+ projection: projection ?? self.projection,
211+ selection: selection ?? self.selection,
212+ title: title ?? self.title,
213+ transform: transform ?? self.transform,
214+ width: width ?? self.width,
215+ layer: layer ?? self.layer,
216+ resolve: resolve ?? self.resolve,
217+ facet: facet ?? self.facet,
218+ spec: spec ?? self.spec,
219+ topLevelRepeat: topLevelRepeat ?? self.topLevelRepeat,
220+ vconcat: vconcat ?? self.vconcat,
221+ hconcat: hconcat ?? self.hconcat
222+ )
223+ }
224+
225+ func jsonData() throws -> Data {
226+ return try newJSONEncoder().encode(self)
227+ }
228+
229+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
230+ return String(data: try self.jsonData(), encoding: encoding)
231+ }
232+}
233+
234+/// Sets how the visualization size should be determined. If a string, should be one of
235+/// `"pad"`, `"fit"` or `"none"`.
236+/// Object values can additionally specify parameters for content sizing and automatic
237+/// resizing.
238+/// `"fit"` is only supported for single and layered views that don't use `rangeStep`.
239+///
240+/// __Default value__: `pad`
241+enum Autosize: Codable {
242+ case autoSizeParams(AutoSizeParams)
243+ case enumeration(AutosizeType)
244+
245+ init(from decoder: Decoder) throws {
246+ let container = try decoder.singleValueContainer()
247+ if let x = try? container.decode(AutosizeType.self) {
248+ self = .enumeration(x)
249+ return
250+ }
251+ if let x = try? container.decode(AutoSizeParams.self) {
252+ self = .autoSizeParams(x)
253+ return
254+ }
255+ throw DecodingError.typeMismatch(Autosize.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Autosize"))
256+ }
257+
258+ func encode(to encoder: Encoder) throws {
259+ var container = encoder.singleValueContainer()
260+ switch self {
261+ case .autoSizeParams(let x):
262+ try container.encode(x)
263+ case .enumeration(let x):
264+ try container.encode(x)
265+ }
266+ }
267+}
268+
269+// MARK: - AutoSizeParams
270+struct AutoSizeParams: Codable {
271+ /// Determines how size calculation should be performed, one of `"content"` or `"padding"`.
272+ /// The default setting (`"content"`) interprets the width and height settings as the data
273+ /// rectangle (plotting) dimensions, to which padding is then added. In contrast, the
274+ /// `"padding"` setting includes the padding within the view size calculations, such that the
275+ /// width and height settings indicate the **total** intended size of the view.
276+ ///
277+ /// __Default value__: `"content"`
278+ let contains: Contains?
279+ /// A boolean flag indicating if autosize layout should be re-calculated on every view
280+ /// update.
281+ ///
282+ /// __Default value__: `false`
283+ let resize: Bool?
284+ /// The sizing format type. One of `"pad"`, `"fit"` or `"none"`. See the [autosize
285+ /// type](https://vega.github.io/vega-lite/docs/size.html#autosize) documentation for
286+ /// descriptions of each.
287+ ///
288+ /// __Default value__: `"pad"`
289+ let type: AutosizeType?
290+
291+ enum CodingKeys: String, CodingKey {
292+ case contains = "contains"
293+ case resize = "resize"
294+ case type = "type"
295+ }
296+}
297+
298+// MARK: AutoSizeParams convenience initializers and mutators
299+
300+extension AutoSizeParams {
301+ init(data: Data) throws {
302+ self = try newJSONDecoder().decode(AutoSizeParams.self, from: data)
303+ }
304+
305+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
306+ guard let data = json.data(using: encoding) else {
307+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
308+ }
309+ try self.init(data: data)
310+ }
311+
312+ init(fromURL url: URL) throws {
313+ try self.init(data: try Data(contentsOf: url))
314+ }
315+
316+ func with(
317+ contains: Contains?? = nil,
318+ resize: Bool?? = nil,
319+ type: AutosizeType?? = nil
320+ ) -> AutoSizeParams {
321+ return AutoSizeParams(
322+ contains: contains ?? self.contains,
323+ resize: resize ?? self.resize,
324+ type: type ?? self.type
325+ )
326+ }
327+
328+ func jsonData() throws -> Data {
329+ return try newJSONEncoder().encode(self)
330+ }
331+
332+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
333+ return String(data: try self.jsonData(), encoding: encoding)
334+ }
335+}
336+
337+/// Determines how size calculation should be performed, one of `"content"` or `"padding"`.
338+/// The default setting (`"content"`) interprets the width and height settings as the data
339+/// rectangle (plotting) dimensions, to which padding is then added. In contrast, the
340+/// `"padding"` setting includes the padding within the view size calculations, such that the
341+/// width and height settings indicate the **total** intended size of the view.
342+///
343+/// __Default value__: `"content"`
344+enum Contains: String, Codable {
345+ case content = "content"
346+ case padding = "padding"
347+}
348+
349+/// The sizing format type. One of `"pad"`, `"fit"` or `"none"`. See the [autosize
350+/// type](https://vega.github.io/vega-lite/docs/size.html#autosize) documentation for
351+/// descriptions of each.
352+///
353+/// __Default value__: `"pad"`
354+enum AutosizeType: String, Codable {
355+ case pad = "pad"
356+ case fit = "fit"
357+ case none = "none"
358+}
359+
360+/// Vega-Lite configuration object. This property can only be defined at the top-level of a
361+/// specification.
362+// MARK: - Config
363+struct Config: Codable {
364+ /// Area-Specific Config
365+ let area: MarkConfig?
366+ /// Sets how the visualization size should be determined. If a string, should be one of
367+ /// `"pad"`, `"fit"` or `"none"`.
368+ /// Object values can additionally specify parameters for content sizing and automatic
369+ /// resizing.
370+ /// `"fit"` is only supported for single and layered views that don't use `rangeStep`.
371+ ///
372+ /// __Default value__: `pad`
373+ let autosize: Autosize?
374+ /// Axis configuration, which determines default properties for all `x` and `y`
375+ /// [axes](axis.html). For a full list of axis configuration options, please see the
376+ /// [corresponding section of the axis documentation](axis.html#config).
377+ let axis: AxisConfig?
378+ /// Specific axis config for axes with "band" scales.
379+ let axisBand: VGAxisConfig?
380+ /// Specific axis config for x-axis along the bottom edge of the chart.
381+ let axisBottom: VGAxisConfig?
382+ /// Specific axis config for y-axis along the left edge of the chart.
383+ let axisLeft: VGAxisConfig?
384+ /// Specific axis config for y-axis along the right edge of the chart.
385+ let axisRight: VGAxisConfig?
386+ /// Specific axis config for x-axis along the top edge of the chart.
387+ let axisTop: VGAxisConfig?
388+ /// X-axis specific config.
389+ let axisX: VGAxisConfig?
390+ /// Y-axis specific config.
391+ let axisY: VGAxisConfig?
392+ /// CSS color property to use as the background of visualization.
393+ ///
394+ /// __Default value:__ none (transparent)
395+ let background: String?
396+ /// Bar-Specific Config
397+ let bar: BarConfig?
398+ /// Circle-Specific Config
399+ let circle: MarkConfig?
400+ /// Default axis and legend title for count fields.
401+ ///
402+ /// __Default value:__ `'Number of Records'`.
403+ let countTitle: String?
404+ /// Defines how Vega-Lite generates title for fields. There are three possible styles:
405+ ///
406+ /// - `"verbal"` (Default) - displays function in a verbal style (e.g., "Sum of field",
407+ /// "Year-month of date", "field (binned)").
408+ /// - `"function"` - displays function using parentheses and capitalized texts (e.g.,
409+ /// "SUM(field)", "YEARMONTH(date)", "BIN(field)").
410+ /// - `"plain"` - displays only the field name without functions (e.g., "field", "date",
411+ /// "field").
412+ let fieldTitle: FieldTitle?
413+ /// Geoshape-Specific Config
414+ let geoshape: MarkConfig?
415+ /// Defines how Vega-Lite should handle invalid values (`null` and `NaN`).
416+ /// - If set to `"filter"` (default), all data items with null values are filtered.
417+ /// - If `null`, all data items are included. In this case, invalid values will be
418+ /// interpreted as zeroes.
419+ let invalidValues: InvalidValues?
420+ /// Legend configuration, which determines default properties for all [legends](legend.html).
421+ /// For a full list of legend configuration options, please see the [corresponding section of
422+ /// in the legend documentation](legend.html#config).
423+ let legend: LegendConfig?
424+ /// Line-Specific Config
425+ let line: MarkConfig?
426+ /// Mark Config
427+ let mark: MarkConfig?
428+ /// D3 Number format for axis labels and text tables. For example "s" for SI units. Use [D3's
429+ /// number format pattern](https://github.com/d3/d3-format#locale_format).
430+ let numberFormat: String?
431+ /// The default visualization padding, in pixels, from the edge of the visualization canvas
432+ /// to the data rectangle. If a number, specifies padding for all sides.
433+ /// If an object, the value should have the format `{"left": 5, "top": 5, "right": 5,
434+ /// "bottom": 5}` to specify padding for each side of the visualization.
435+ ///
436+ /// __Default value__: `5`
437+ let padding: Padding?
438+ /// Point-Specific Config
439+ let point: MarkConfig?
440+ /// Projection configuration, which determines default properties for all
441+ /// [projections](projection.html). For a full list of projection configuration options,
442+ /// please see the [corresponding section of the projection
443+ /// documentation](projection.html#config).
444+ let projection: ProjectionConfig?
445+ /// An object hash that defines default range arrays or schemes for using with scales.
446+ /// For a full list of scale range configuration options, please see the [corresponding
447+ /// section of the scale documentation](scale.html#config).
448+ let range: [String: RangeConfigValue]?
449+ /// Rect-Specific Config
450+ let rect: MarkConfig?
451+ /// Rule-Specific Config
452+ let rule: MarkConfig?
453+ /// Scale configuration determines default properties for all [scales](scale.html). For a
454+ /// full list of scale configuration options, please see the [corresponding section of the
455+ /// scale documentation](scale.html#config).
456+ let scale: ScaleConfig?
457+ /// An object hash for defining default properties for each type of selections.
458+ let selection: SelectionConfig?
459+ /// Square-Specific Config
460+ let square: MarkConfig?
461+ /// Default stack offset for stackable mark.
462+ let stack: StackOffset?
463+ /// An object hash that defines key-value mappings to determine default properties for marks
464+ /// with a given [style](mark.html#mark-def). The keys represent styles names; the value are
465+ /// valid [mark configuration objects](mark.html#config).
466+ let style: [String: VGMarkConfig]?
467+ /// Text-Specific Config
468+ let text: TextConfig?
469+ /// Tick-Specific Config
470+ let tick: TickConfig?
471+ /// Default datetime format for axis and legend labels. The format can be set directly on
472+ /// each axis and legend. Use [D3's time format
473+ /// pattern](https://github.com/d3/d3-time-format#locale_format).
474+ ///
475+ /// __Default value:__ `'%b %d, %Y'`.
476+ let timeFormat: String?
477+ /// Title configuration, which determines default properties for all [titles](title.html).
478+ /// For a full list of title configuration options, please see the [corresponding section of
479+ /// the title documentation](title.html#config).
480+ let title: VGTitleConfig?
481+ /// Default properties for [single view plots](spec.html#single).
482+ let view: ViewConfig?
483+
484+ enum CodingKeys: String, CodingKey {
485+ case area = "area"
486+ case autosize = "autosize"
487+ case axis = "axis"
488+ case axisBand = "axisBand"
489+ case axisBottom = "axisBottom"
490+ case axisLeft = "axisLeft"
491+ case axisRight = "axisRight"
492+ case axisTop = "axisTop"
493+ case axisX = "axisX"
494+ case axisY = "axisY"
495+ case background = "background"
496+ case bar = "bar"
497+ case circle = "circle"
498+ case countTitle = "countTitle"
499+ case fieldTitle = "fieldTitle"
500+ case geoshape = "geoshape"
501+ case invalidValues = "invalidValues"
502+ case legend = "legend"
503+ case line = "line"
504+ case mark = "mark"
505+ case numberFormat = "numberFormat"
506+ case padding = "padding"
507+ case point = "point"
508+ case projection = "projection"
509+ case range = "range"
510+ case rect = "rect"
511+ case rule = "rule"
512+ case scale = "scale"
513+ case selection = "selection"
514+ case square = "square"
515+ case stack = "stack"
516+ case style = "style"
517+ case text = "text"
518+ case tick = "tick"
519+ case timeFormat = "timeFormat"
520+ case title = "title"
521+ case view = "view"
522+ }
523+}
524+
525+// MARK: Config convenience initializers and mutators
526+
527+extension Config {
528+ init(data: Data) throws {
529+ self = try newJSONDecoder().decode(Config.self, from: data)
530+ }
531+
532+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
533+ guard let data = json.data(using: encoding) else {
534+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
535+ }
536+ try self.init(data: data)
537+ }
538+
539+ init(fromURL url: URL) throws {
540+ try self.init(data: try Data(contentsOf: url))
541+ }
542+
543+ func with(
544+ area: MarkConfig?? = nil,
545+ autosize: Autosize?? = nil,
546+ axis: AxisConfig?? = nil,
547+ axisBand: VGAxisConfig?? = nil,
548+ axisBottom: VGAxisConfig?? = nil,
549+ axisLeft: VGAxisConfig?? = nil,
550+ axisRight: VGAxisConfig?? = nil,
551+ axisTop: VGAxisConfig?? = nil,
552+ axisX: VGAxisConfig?? = nil,
553+ axisY: VGAxisConfig?? = nil,
554+ background: String?? = nil,
555+ bar: BarConfig?? = nil,
556+ circle: MarkConfig?? = nil,
557+ countTitle: String?? = nil,
558+ fieldTitle: FieldTitle?? = nil,
559+ geoshape: MarkConfig?? = nil,
560+ invalidValues: InvalidValues?? = nil,
561+ legend: LegendConfig?? = nil,
562+ line: MarkConfig?? = nil,
563+ mark: MarkConfig?? = nil,
564+ numberFormat: String?? = nil,
565+ padding: Padding?? = nil,
566+ point: MarkConfig?? = nil,
567+ projection: ProjectionConfig?? = nil,
568+ range: [String: RangeConfigValue]?? = nil,
569+ rect: MarkConfig?? = nil,
570+ rule: MarkConfig?? = nil,
571+ scale: ScaleConfig?? = nil,
572+ selection: SelectionConfig?? = nil,
573+ square: MarkConfig?? = nil,
574+ stack: StackOffset?? = nil,
575+ style: [String: VGMarkConfig]?? = nil,
576+ text: TextConfig?? = nil,
577+ tick: TickConfig?? = nil,
578+ timeFormat: String?? = nil,
579+ title: VGTitleConfig?? = nil,
580+ view: ViewConfig?? = nil
581+ ) -> Config {
582+ return Config(
583+ area: area ?? self.area,
584+ autosize: autosize ?? self.autosize,
585+ axis: axis ?? self.axis,
586+ axisBand: axisBand ?? self.axisBand,
587+ axisBottom: axisBottom ?? self.axisBottom,
588+ axisLeft: axisLeft ?? self.axisLeft,
589+ axisRight: axisRight ?? self.axisRight,
590+ axisTop: axisTop ?? self.axisTop,
591+ axisX: axisX ?? self.axisX,
592+ axisY: axisY ?? self.axisY,
593+ background: background ?? self.background,
594+ bar: bar ?? self.bar,
595+ circle: circle ?? self.circle,
596+ countTitle: countTitle ?? self.countTitle,
597+ fieldTitle: fieldTitle ?? self.fieldTitle,
598+ geoshape: geoshape ?? self.geoshape,
599+ invalidValues: invalidValues ?? self.invalidValues,
600+ legend: legend ?? self.legend,
601+ line: line ?? self.line,
602+ mark: mark ?? self.mark,
603+ numberFormat: numberFormat ?? self.numberFormat,
604+ padding: padding ?? self.padding,
605+ point: point ?? self.point,
606+ projection: projection ?? self.projection,
607+ range: range ?? self.range,
608+ rect: rect ?? self.rect,
609+ rule: rule ?? self.rule,
610+ scale: scale ?? self.scale,
611+ selection: selection ?? self.selection,
612+ square: square ?? self.square,
613+ stack: stack ?? self.stack,
614+ style: style ?? self.style,
615+ text: text ?? self.text,
616+ tick: tick ?? self.tick,
617+ timeFormat: timeFormat ?? self.timeFormat,
618+ title: title ?? self.title,
619+ view: view ?? self.view
620+ )
621+ }
622+
623+ func jsonData() throws -> Data {
624+ return try newJSONEncoder().encode(self)
625+ }
626+
627+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
628+ return String(data: try self.jsonData(), encoding: encoding)
629+ }
630+}
631+
632+/// Area-Specific Config
633+///
634+/// Circle-Specific Config
635+///
636+/// Geoshape-Specific Config
637+///
638+/// Line-Specific Config
639+///
640+/// Mark Config
641+///
642+/// Point-Specific Config
643+///
644+/// Rect-Specific Config
645+///
646+/// Rule-Specific Config
647+///
648+/// Square-Specific Config
649+// MARK: - MarkConfig
650+struct MarkConfig: Codable {
651+ /// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
652+ let align: HorizontalAlign?
653+ /// The rotation angle of the text, in degrees.
654+ let angle: Double?
655+ /// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
656+ ///
657+ /// __Default value:__ `"middle"`
658+ let baseline: VerticalAlign?
659+ /// Default color. Note that `fill` and `stroke` have higher precedence than `color` and
660+ /// will override `color`.
661+ ///
662+ /// __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
663+ ///
664+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
665+ let color: String?
666+ /// The mouse cursor used over the mark. Any valid [CSS cursor
667+ /// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
668+ let cursor: Cursor?
669+ /// The horizontal offset, in pixels, between the text label and its anchor point. The offset
670+ /// is applied after rotation by the _angle_ property.
671+ let dx: Double?
672+ /// The vertical offset, in pixels, between the text label and its anchor point. The offset
673+ /// is applied after rotation by the _angle_ property.
674+ let dy: Double?
675+ /// Default Fill Color. This has higher precedence than config.color
676+ ///
677+ /// __Default value:__ (None)
678+ let fill: String?
679+ /// Whether the mark's color should be used as fill color instead of stroke color.
680+ ///
681+ /// __Default value:__ `true` for all marks except `point` and `false` for `point`.
682+ ///
683+ /// __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
684+ ///
685+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
686+ let filled: Bool?
687+ /// The fill opacity (value between [0,1]).
688+ ///
689+ /// __Default value:__ `1`
690+ let fillOpacity: Double?
691+ /// The typeface to set the text in (e.g., `"Helvetica Neue"`).
692+ let font: String?
693+ /// The font size, in pixels.
694+ let fontSize: Double?
695+ /// The font style (e.g., `"italic"`).
696+ let fontStyle: FontStyle?
697+ /// The font weight (e.g., `"bold"`).
698+ let fontWeight: FontWeightUnion?
699+ /// A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
700+ let href: String?
701+ /// The line interpolation method to use for line and area marks. One of the following:
702+ /// - `"linear"`: piecewise linear segments, as in a polyline.
703+ /// - `"linear-closed"`: close the linear segments to form a polygon.
704+ /// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
705+ /// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
706+ /// function.
707+ /// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
708+ /// function.
709+ /// - `"basis"`: a B-spline, with control point duplication on the ends.
710+ /// - `"basis-open"`: an open B-spline; may not intersect the start or end.
711+ /// - `"basis-closed"`: a closed B-spline, as in a loop.
712+ /// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
713+ /// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
714+ /// will intersect other control points.
715+ /// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
716+ /// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
717+ /// spline.
718+ /// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
719+ let interpolate: Interpolate?
720+ /// The maximum length of the text mark in pixels (default 0, indicating no limit). The text
721+ /// value will be automatically truncated if the rendered size exceeds the limit.
722+ let limit: Double?
723+ /// The overall opacity (value between [0,1]).
724+ ///
725+ /// __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
726+ /// `square` marks or layered `bar` charts and `1` otherwise.
727+ let opacity: Double?
728+ /// The orientation of a non-stacked bar, tick, area, and line charts.
729+ /// The value is either horizontal (default) or vertical.
730+ /// - For bar, rule and tick, this determines whether the size of the bar and tick
731+ /// should be applied to x or y dimension.
732+ /// - For area, this property determines the orient property of the Vega output.
733+ /// - For line, this property determines the sort order of the points in the line
734+ /// if `config.sortLineBy` is not specified.
735+ /// For stacked charts, this is always determined by the orientation of the stack;
736+ /// therefore explicitly specified value will be ignored.
737+ let orient: Orient?
738+ /// Polar coordinate radial offset, in pixels, of the text label from the origin determined
739+ /// by the `x` and `y` properties.
740+ let radius: Double?
741+ /// The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
742+ /// `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
743+ ///
744+ /// __Default value:__ `"circle"`
745+ let shape: String?
746+ /// The pixel area each the point/circle/square.
747+ /// For example: in the case of circles, the radius is determined in part by the square root
748+ /// of the size value.
749+ ///
750+ /// __Default value:__ `30`
751+ let size: Double?
752+ /// Default Stroke Color. This has higher precedence than config.color
753+ ///
754+ /// __Default value:__ (None)
755+ let stroke: String?
756+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
757+ let strokeDash: [Double]?
758+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
759+ let strokeDashOffset: Double?
760+ /// The stroke opacity (value between [0,1]).
761+ ///
762+ /// __Default value:__ `1`
763+ let strokeOpacity: Double?
764+ /// The stroke width, in pixels.
765+ let strokeWidth: Double?
766+ /// Depending on the interpolation type, sets the tension parameter (for line and area marks).
767+ let tension: Double?
768+ /// Placeholder text if the `text` channel is not specified
769+ let text: String?
770+ /// Polar coordinate angle, in radians, of the text label from the origin determined by the
771+ /// `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
772+ /// `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
773+ /// indicating "north".
774+ let theta: Double?
775+
776+ enum CodingKeys: String, CodingKey {
777+ case align = "align"
778+ case angle = "angle"
779+ case baseline = "baseline"
780+ case color = "color"
781+ case cursor = "cursor"
782+ case dx = "dx"
783+ case dy = "dy"
784+ case fill = "fill"
785+ case filled = "filled"
786+ case fillOpacity = "fillOpacity"
787+ case font = "font"
788+ case fontSize = "fontSize"
789+ case fontStyle = "fontStyle"
790+ case fontWeight = "fontWeight"
791+ case href = "href"
792+ case interpolate = "interpolate"
793+ case limit = "limit"
794+ case opacity = "opacity"
795+ case orient = "orient"
796+ case radius = "radius"
797+ case shape = "shape"
798+ case size = "size"
799+ case stroke = "stroke"
800+ case strokeDash = "strokeDash"
801+ case strokeDashOffset = "strokeDashOffset"
802+ case strokeOpacity = "strokeOpacity"
803+ case strokeWidth = "strokeWidth"
804+ case tension = "tension"
805+ case text = "text"
806+ case theta = "theta"
807+ }
808+}
809+
810+// MARK: MarkConfig convenience initializers and mutators
811+
812+extension MarkConfig {
813+ init(data: Data) throws {
814+ self = try newJSONDecoder().decode(MarkConfig.self, from: data)
815+ }
816+
817+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
818+ guard let data = json.data(using: encoding) else {
819+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
820+ }
821+ try self.init(data: data)
822+ }
823+
824+ init(fromURL url: URL) throws {
825+ try self.init(data: try Data(contentsOf: url))
826+ }
827+
828+ func with(
829+ align: HorizontalAlign?? = nil,
830+ angle: Double?? = nil,
831+ baseline: VerticalAlign?? = nil,
832+ color: String?? = nil,
833+ cursor: Cursor?? = nil,
834+ dx: Double?? = nil,
835+ dy: Double?? = nil,
836+ fill: String?? = nil,
837+ filled: Bool?? = nil,
838+ fillOpacity: Double?? = nil,
839+ font: String?? = nil,
840+ fontSize: Double?? = nil,
841+ fontStyle: FontStyle?? = nil,
842+ fontWeight: FontWeightUnion?? = nil,
843+ href: String?? = nil,
844+ interpolate: Interpolate?? = nil,
845+ limit: Double?? = nil,
846+ opacity: Double?? = nil,
847+ orient: Orient?? = nil,
848+ radius: Double?? = nil,
849+ shape: String?? = nil,
850+ size: Double?? = nil,
851+ stroke: String?? = nil,
852+ strokeDash: [Double]?? = nil,
853+ strokeDashOffset: Double?? = nil,
854+ strokeOpacity: Double?? = nil,
855+ strokeWidth: Double?? = nil,
856+ tension: Double?? = nil,
857+ text: String?? = nil,
858+ theta: Double?? = nil
859+ ) -> MarkConfig {
860+ return MarkConfig(
861+ align: align ?? self.align,
862+ angle: angle ?? self.angle,
863+ baseline: baseline ?? self.baseline,
864+ color: color ?? self.color,
865+ cursor: cursor ?? self.cursor,
866+ dx: dx ?? self.dx,
867+ dy: dy ?? self.dy,
868+ fill: fill ?? self.fill,
869+ filled: filled ?? self.filled,
870+ fillOpacity: fillOpacity ?? self.fillOpacity,
871+ font: font ?? self.font,
872+ fontSize: fontSize ?? self.fontSize,
873+ fontStyle: fontStyle ?? self.fontStyle,
874+ fontWeight: fontWeight ?? self.fontWeight,
875+ href: href ?? self.href,
876+ interpolate: interpolate ?? self.interpolate,
877+ limit: limit ?? self.limit,
878+ opacity: opacity ?? self.opacity,
879+ orient: orient ?? self.orient,
880+ radius: radius ?? self.radius,
881+ shape: shape ?? self.shape,
882+ size: size ?? self.size,
883+ stroke: stroke ?? self.stroke,
884+ strokeDash: strokeDash ?? self.strokeDash,
885+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
886+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
887+ strokeWidth: strokeWidth ?? self.strokeWidth,
888+ tension: tension ?? self.tension,
889+ text: text ?? self.text,
890+ theta: theta ?? self.theta
891+ )
892+ }
893+
894+ func jsonData() throws -> Data {
895+ return try newJSONEncoder().encode(self)
896+ }
897+
898+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
899+ return String(data: try self.jsonData(), encoding: encoding)
900+ }
901+}
902+
903+/// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
904+enum HorizontalAlign: String, Codable {
905+ case horizontalAlignLeft = "left"
906+ case horizontalAlignRight = "right"
907+ case center = "center"
908+}
909+
910+/// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
911+///
912+/// __Default value:__ `"middle"`
913+///
914+/// Vertical text baseline for title text.
915+enum VerticalAlign: String, Codable {
916+ case top = "top"
917+ case middle = "middle"
918+ case bottom = "bottom"
919+}
920+
921+/// The mouse cursor used over the mark. Any valid [CSS cursor
922+/// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
923+enum Cursor: String, Codable {
924+ case auto = "auto"
925+ case cursorDefault = "default"
926+ case none = "none"
927+ case contextMenu = "context-menu"
928+ case help = "help"
929+ case pointer = "pointer"
930+ case progress = "progress"
931+ case wait = "wait"
932+ case cell = "cell"
933+ case crosshair = "crosshair"
934+ case text = "text"
935+ case verticalText = "vertical-text"
936+ case alias = "alias"
937+ case copy = "copy"
938+ case move = "move"
939+ case noDrop = "no-drop"
940+ case notAllowed = "not-allowed"
941+ case eResize = "e-resize"
942+ case nResize = "n-resize"
943+ case neResize = "ne-resize"
944+ case nwResize = "nw-resize"
945+ case sResize = "s-resize"
946+ case seResize = "se-resize"
947+ case swResize = "sw-resize"
948+ case wResize = "w-resize"
949+ case ewResize = "ew-resize"
950+ case nsResize = "ns-resize"
951+ case neswResize = "nesw-resize"
952+ case nwseResize = "nwse-resize"
953+ case colResize = "col-resize"
954+ case rowResize = "row-resize"
955+ case allScroll = "all-scroll"
956+ case zoomIn = "zoom-in"
957+ case zoomOut = "zoom-out"
958+ case grab = "grab"
959+ case grabbing = "grabbing"
960+}
961+
962+/// The font style (e.g., `"italic"`).
963+enum FontStyle: String, Codable {
964+ case normal = "normal"
965+ case italic = "italic"
966+}
967+
968+/// The font weight (e.g., `"bold"`).
969+///
970+/// Font weight for title text.
971+enum FontWeightUnion: Codable {
972+ case double(Double)
973+ case enumeration(FontWeight)
974+
975+ init(from decoder: Decoder) throws {
976+ let container = try decoder.singleValueContainer()
977+ if let x = try? container.decode(Double.self) {
978+ self = .double(x)
979+ return
980+ }
981+ if let x = try? container.decode(FontWeight.self) {
982+ self = .enumeration(x)
983+ return
984+ }
985+ throw DecodingError.typeMismatch(FontWeightUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for FontWeightUnion"))
986+ }
987+
988+ func encode(to encoder: Encoder) throws {
989+ var container = encoder.singleValueContainer()
990+ switch self {
991+ case .double(let x):
992+ try container.encode(x)
993+ case .enumeration(let x):
994+ try container.encode(x)
995+ }
996+ }
997+}
998+
999+enum FontWeight: String, Codable {
1000+ case normal = "normal"
1001+ case bold = "bold"
1002+}
1003+
1004+/// The line interpolation method to use for line and area marks. One of the following:
1005+/// - `"linear"`: piecewise linear segments, as in a polyline.
1006+/// - `"linear-closed"`: close the linear segments to form a polygon.
1007+/// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
1008+/// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
1009+/// function.
1010+/// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
1011+/// function.
1012+/// - `"basis"`: a B-spline, with control point duplication on the ends.
1013+/// - `"basis-open"`: an open B-spline; may not intersect the start or end.
1014+/// - `"basis-closed"`: a closed B-spline, as in a loop.
1015+/// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
1016+/// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
1017+/// will intersect other control points.
1018+/// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
1019+/// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
1020+/// spline.
1021+/// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
1022+enum Interpolate: String, Codable {
1023+ case linear = "linear"
1024+ case linearClosed = "linear-closed"
1025+ case step = "step"
1026+ case stepBefore = "step-before"
1027+ case stepAfter = "step-after"
1028+ case basis = "basis"
1029+ case basisOpen = "basis-open"
1030+ case basisClosed = "basis-closed"
1031+ case cardinal = "cardinal"
1032+ case cardinalOpen = "cardinal-open"
1033+ case cardinalClosed = "cardinal-closed"
1034+ case bundle = "bundle"
1035+ case monotone = "monotone"
1036+}
1037+
1038+/// The orientation of a non-stacked bar, tick, area, and line charts.
1039+/// The value is either horizontal (default) or vertical.
1040+/// - For bar, rule and tick, this determines whether the size of the bar and tick
1041+/// should be applied to x or y dimension.
1042+/// - For area, this property determines the orient property of the Vega output.
1043+/// - For line, this property determines the sort order of the points in the line
1044+/// if `config.sortLineBy` is not specified.
1045+/// For stacked charts, this is always determined by the orientation of the stack;
1046+/// therefore explicitly specified value will be ignored.
1047+enum Orient: String, Codable {
1048+ case horizontal = "horizontal"
1049+ case vertical = "vertical"
1050+}
1051+
1052+/// Axis configuration, which determines default properties for all `x` and `y`
1053+/// [axes](axis.html). For a full list of axis configuration options, please see the
1054+/// [corresponding section of the axis documentation](axis.html#config).
1055+// MARK: - AxisConfig
1056+struct AxisConfig: Codable {
1057+ /// An interpolation fraction indicating where, for `band` scales, axis ticks should be
1058+ /// positioned. A value of `0` places ticks at the left edge of their bands. A value of `0.5`
1059+ /// places ticks in the middle of their bands.
1060+ let bandPosition: Double?
1061+ /// A boolean flag indicating if the domain (the axis baseline) should be included as part of
1062+ /// the axis.
1063+ ///
1064+ /// __Default value:__ `true`
1065+ let domain: Bool?
1066+ /// Color of axis domain line.
1067+ ///
1068+ /// __Default value:__ (none, using Vega default).
1069+ let domainColor: String?
1070+ /// Stroke width of axis domain line
1071+ ///
1072+ /// __Default value:__ (none, using Vega default).
1073+ let domainWidth: Double?
1074+ /// A boolean flag indicating if grid lines should be included as part of the axis
1075+ ///
1076+ /// __Default value:__ `true` for [continuous scales](scale.html#continuous) that are not
1077+ /// binned; otherwise, `false`.
1078+ let grid: Bool?
1079+ /// Color of gridlines.
1080+ let gridColor: String?
1081+ /// The offset (in pixels) into which to begin drawing with the grid dash array.
1082+ let gridDash: [Double]?
1083+ /// The stroke opacity of grid (value between [0,1])
1084+ ///
1085+ /// __Default value:__ (`1` by default)
1086+ let gridOpacity: Double?
1087+ /// The grid width, in pixels.
1088+ let gridWidth: Double?
1089+ /// The rotation angle of the axis labels.
1090+ ///
1091+ /// __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
1092+ let labelAngle: Double?
1093+ /// Indicates if labels should be hidden if they exceed the axis range. If `false `(the
1094+ /// default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
1095+ /// they exceed the axis range by more than 1 pixel. If this property is a number, it
1096+ /// specifies the pixel tolerance: the maximum amount by which a label bounding box may
1097+ /// exceed the axis range.
1098+ ///
1099+ /// __Default value:__ `false`.
1100+ let labelBound: Label?
1101+ /// The color of the tick label, can be in hex color code or regular color name.
1102+ let labelColor: String?
1103+ /// Indicates if the first and last axis labels should be aligned flush with the scale range.
1104+ /// Flush alignment for a horizontal axis will left-align the first label and right-align the
1105+ /// last label. For vertical axes, bottom and top text baselines are applied instead. If this
1106+ /// property is a number, it also indicates the number of pixels by which to offset the first
1107+ /// and last labels; for example, a value of 2 will flush-align the first and last labels and
1108+ /// also push them 2 pixels outward from the center of the axis. The additional adjustment
1109+ /// can sometimes help the labels better visually group with corresponding axis ticks.
1110+ ///
1111+ /// __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
1112+ let labelFlush: Label?
1113+ /// The font of the tick label.
1114+ let labelFont: String?
1115+ /// The font size of the label, in pixels.
1116+ let labelFontSize: Double?
1117+ /// Maximum allowed pixel width of axis tick labels.
1118+ let labelLimit: Double?
1119+ /// The strategy to use for resolving overlap of axis labels. If `false` (the default), no
1120+ /// overlap reduction is attempted. If set to `true` or `"parity"`, a strategy of removing
1121+ /// every other label is used (this works well for standard linear axes). If set to
1122+ /// `"greedy"`, a linear scan of the labels is performed, removing any labels that overlaps
1123+ /// with the last visible label (this often works better for log-scaled axes).
1124+ ///
1125+ /// __Default value:__ `true` for non-nominal fields with non-log scales; `"greedy"` for log
1126+ /// scales; otherwise `false`.
1127+ let labelOverlap: LabelOverlapUnion?
1128+ /// The padding, in pixels, between axis and text labels.
1129+ let labelPadding: Double?
1130+ /// A boolean flag indicating if labels should be included as part of the axis.
1131+ ///
1132+ /// __Default value:__ `true`.
1133+ let labels: Bool?
1134+ /// The maximum extent in pixels that axis ticks and labels should use. This determines a
1135+ /// maximum offset value for axis titles.
1136+ ///
1137+ /// __Default value:__ `undefined`.
1138+ let maxExtent: Double?
1139+ /// The minimum extent in pixels that axis ticks and labels should use. This determines a
1140+ /// minimum offset value for axis titles.
1141+ ///
1142+ /// __Default value:__ `30` for y-axis; `undefined` for x-axis.
1143+ let minExtent: Double?
1144+ /// Whether month names and weekday names should be abbreviated.
1145+ ///
1146+ /// __Default value:__ `false`
1147+ let shortTimeLabels: Bool?
1148+ /// The color of the axis's tick.
1149+ let tickColor: String?
1150+ /// Boolean flag indicating if pixel position values should be rounded to the nearest integer.
1151+ let tickRound: Bool?
1152+ /// Boolean value that determines whether the axis should include ticks.
1153+ let ticks: Bool?
1154+ /// The size in pixels of axis ticks.
1155+ let tickSize: Double?
1156+ /// The width, in pixels, of ticks.
1157+ let tickWidth: Double?
1158+ /// Horizontal text alignment of axis titles.
1159+ let titleAlign: String?
1160+ /// Angle in degrees of axis titles.
1161+ let titleAngle: Double?
1162+ /// Vertical text baseline for axis titles.
1163+ let titleBaseline: String?
1164+ /// Color of the title, can be in hex color code or regular color name.
1165+ let titleColor: String?
1166+ /// Font of the title. (e.g., `"Helvetica Neue"`).
1167+ let titleFont: String?
1168+ /// Font size of the title.
1169+ let titleFontSize: Double?
1170+ /// Font weight of the title. (e.g., `"bold"`).
1171+ let titleFontWeight: TitleFontWeight?
1172+ /// Maximum allowed pixel width of axis titles.
1173+ let titleLimit: Double?
1174+ /// Max length for axis title if the title is automatically generated from the field's
1175+ /// description.
1176+ let titleMaxLength: Double?
1177+ /// The padding, in pixels, between title and axis.
1178+ let titlePadding: Double?
1179+ /// X-coordinate of the axis title relative to the axis group.
1180+ let titleX: Double?
1181+ /// Y-coordinate of the axis title relative to the axis group.
1182+ let titleY: Double?
1183+
1184+ enum CodingKeys: String, CodingKey {
1185+ case bandPosition = "bandPosition"
1186+ case domain = "domain"
1187+ case domainColor = "domainColor"
1188+ case domainWidth = "domainWidth"
1189+ case grid = "grid"
1190+ case gridColor = "gridColor"
1191+ case gridDash = "gridDash"
1192+ case gridOpacity = "gridOpacity"
1193+ case gridWidth = "gridWidth"
1194+ case labelAngle = "labelAngle"
1195+ case labelBound = "labelBound"
1196+ case labelColor = "labelColor"
1197+ case labelFlush = "labelFlush"
1198+ case labelFont = "labelFont"
1199+ case labelFontSize = "labelFontSize"
1200+ case labelLimit = "labelLimit"
1201+ case labelOverlap = "labelOverlap"
1202+ case labelPadding = "labelPadding"
1203+ case labels = "labels"
1204+ case maxExtent = "maxExtent"
1205+ case minExtent = "minExtent"
1206+ case shortTimeLabels = "shortTimeLabels"
1207+ case tickColor = "tickColor"
1208+ case tickRound = "tickRound"
1209+ case ticks = "ticks"
1210+ case tickSize = "tickSize"
1211+ case tickWidth = "tickWidth"
1212+ case titleAlign = "titleAlign"
1213+ case titleAngle = "titleAngle"
1214+ case titleBaseline = "titleBaseline"
1215+ case titleColor = "titleColor"
1216+ case titleFont = "titleFont"
1217+ case titleFontSize = "titleFontSize"
1218+ case titleFontWeight = "titleFontWeight"
1219+ case titleLimit = "titleLimit"
1220+ case titleMaxLength = "titleMaxLength"
1221+ case titlePadding = "titlePadding"
1222+ case titleX = "titleX"
1223+ case titleY = "titleY"
1224+ }
1225+}
1226+
1227+// MARK: AxisConfig convenience initializers and mutators
1228+
1229+extension AxisConfig {
1230+ init(data: Data) throws {
1231+ self = try newJSONDecoder().decode(AxisConfig.self, from: data)
1232+ }
1233+
1234+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
1235+ guard let data = json.data(using: encoding) else {
1236+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
1237+ }
1238+ try self.init(data: data)
1239+ }
1240+
1241+ init(fromURL url: URL) throws {
1242+ try self.init(data: try Data(contentsOf: url))
1243+ }
1244+
1245+ func with(
1246+ bandPosition: Double?? = nil,
1247+ domain: Bool?? = nil,
1248+ domainColor: String?? = nil,
1249+ domainWidth: Double?? = nil,
1250+ grid: Bool?? = nil,
1251+ gridColor: String?? = nil,
1252+ gridDash: [Double]?? = nil,
1253+ gridOpacity: Double?? = nil,
1254+ gridWidth: Double?? = nil,
1255+ labelAngle: Double?? = nil,
1256+ labelBound: Label?? = nil,
1257+ labelColor: String?? = nil,
1258+ labelFlush: Label?? = nil,
1259+ labelFont: String?? = nil,
1260+ labelFontSize: Double?? = nil,
1261+ labelLimit: Double?? = nil,
1262+ labelOverlap: LabelOverlapUnion?? = nil,
1263+ labelPadding: Double?? = nil,
1264+ labels: Bool?? = nil,
1265+ maxExtent: Double?? = nil,
1266+ minExtent: Double?? = nil,
1267+ shortTimeLabels: Bool?? = nil,
1268+ tickColor: String?? = nil,
1269+ tickRound: Bool?? = nil,
1270+ ticks: Bool?? = nil,
1271+ tickSize: Double?? = nil,
1272+ tickWidth: Double?? = nil,
1273+ titleAlign: String?? = nil,
1274+ titleAngle: Double?? = nil,
1275+ titleBaseline: String?? = nil,
1276+ titleColor: String?? = nil,
1277+ titleFont: String?? = nil,
1278+ titleFontSize: Double?? = nil,
1279+ titleFontWeight: TitleFontWeight?? = nil,
1280+ titleLimit: Double?? = nil,
1281+ titleMaxLength: Double?? = nil,
1282+ titlePadding: Double?? = nil,
1283+ titleX: Double?? = nil,
1284+ titleY: Double?? = nil
1285+ ) -> AxisConfig {
1286+ return AxisConfig(
1287+ bandPosition: bandPosition ?? self.bandPosition,
1288+ domain: domain ?? self.domain,
1289+ domainColor: domainColor ?? self.domainColor,
1290+ domainWidth: domainWidth ?? self.domainWidth,
1291+ grid: grid ?? self.grid,
1292+ gridColor: gridColor ?? self.gridColor,
1293+ gridDash: gridDash ?? self.gridDash,
1294+ gridOpacity: gridOpacity ?? self.gridOpacity,
1295+ gridWidth: gridWidth ?? self.gridWidth,
1296+ labelAngle: labelAngle ?? self.labelAngle,
1297+ labelBound: labelBound ?? self.labelBound,
1298+ labelColor: labelColor ?? self.labelColor,
1299+ labelFlush: labelFlush ?? self.labelFlush,
1300+ labelFont: labelFont ?? self.labelFont,
1301+ labelFontSize: labelFontSize ?? self.labelFontSize,
1302+ labelLimit: labelLimit ?? self.labelLimit,
1303+ labelOverlap: labelOverlap ?? self.labelOverlap,
1304+ labelPadding: labelPadding ?? self.labelPadding,
1305+ labels: labels ?? self.labels,
1306+ maxExtent: maxExtent ?? self.maxExtent,
1307+ minExtent: minExtent ?? self.minExtent,
1308+ shortTimeLabels: shortTimeLabels ?? self.shortTimeLabels,
1309+ tickColor: tickColor ?? self.tickColor,
1310+ tickRound: tickRound ?? self.tickRound,
1311+ ticks: ticks ?? self.ticks,
1312+ tickSize: tickSize ?? self.tickSize,
1313+ tickWidth: tickWidth ?? self.tickWidth,
1314+ titleAlign: titleAlign ?? self.titleAlign,
1315+ titleAngle: titleAngle ?? self.titleAngle,
1316+ titleBaseline: titleBaseline ?? self.titleBaseline,
1317+ titleColor: titleColor ?? self.titleColor,
1318+ titleFont: titleFont ?? self.titleFont,
1319+ titleFontSize: titleFontSize ?? self.titleFontSize,
1320+ titleFontWeight: titleFontWeight ?? self.titleFontWeight,
1321+ titleLimit: titleLimit ?? self.titleLimit,
1322+ titleMaxLength: titleMaxLength ?? self.titleMaxLength,
1323+ titlePadding: titlePadding ?? self.titlePadding,
1324+ titleX: titleX ?? self.titleX,
1325+ titleY: titleY ?? self.titleY
1326+ )
1327+ }
1328+
1329+ func jsonData() throws -> Data {
1330+ return try newJSONEncoder().encode(self)
1331+ }
1332+
1333+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
1334+ return String(data: try self.jsonData(), encoding: encoding)
1335+ }
1336+}
1337+
1338+/// Indicates if labels should be hidden if they exceed the axis range. If `false `(the
1339+/// default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
1340+/// they exceed the axis range by more than 1 pixel. If this property is a number, it
1341+/// specifies the pixel tolerance: the maximum amount by which a label bounding box may
1342+/// exceed the axis range.
1343+///
1344+/// __Default value:__ `false`.
1345+///
1346+/// Indicates if the first and last axis labels should be aligned flush with the scale range.
1347+/// Flush alignment for a horizontal axis will left-align the first label and right-align the
1348+/// last label. For vertical axes, bottom and top text baselines are applied instead. If this
1349+/// property is a number, it also indicates the number of pixels by which to offset the first
1350+/// and last labels; for example, a value of 2 will flush-align the first and last labels and
1351+/// also push them 2 pixels outward from the center of the axis. The additional adjustment
1352+/// can sometimes help the labels better visually group with corresponding axis ticks.
1353+///
1354+/// __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
1355+enum Label: Codable {
1356+ case bool(Bool)
1357+ case double(Double)
1358+
1359+ init(from decoder: Decoder) throws {
1360+ let container = try decoder.singleValueContainer()
1361+ if let x = try? container.decode(Bool.self) {
1362+ self = .bool(x)
1363+ return
1364+ }
1365+ if let x = try? container.decode(Double.self) {
1366+ self = .double(x)
1367+ return
1368+ }
1369+ throw DecodingError.typeMismatch(Label.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Label"))
1370+ }
1371+
1372+ func encode(to encoder: Encoder) throws {
1373+ var container = encoder.singleValueContainer()
1374+ switch self {
1375+ case .bool(let x):
1376+ try container.encode(x)
1377+ case .double(let x):
1378+ try container.encode(x)
1379+ }
1380+ }
1381+}
1382+
1383+enum LabelOverlapUnion: Codable {
1384+ case bool(Bool)
1385+ case enumeration(LabelOverlapEnum)
1386+
1387+ init(from decoder: Decoder) throws {
1388+ let container = try decoder.singleValueContainer()
1389+ if let x = try? container.decode(Bool.self) {
1390+ self = .bool(x)
1391+ return
1392+ }
1393+ if let x = try? container.decode(LabelOverlapEnum.self) {
1394+ self = .enumeration(x)
1395+ return
1396+ }
1397+ throw DecodingError.typeMismatch(LabelOverlapUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for LabelOverlapUnion"))
1398+ }
1399+
1400+ func encode(to encoder: Encoder) throws {
1401+ var container = encoder.singleValueContainer()
1402+ switch self {
1403+ case .bool(let x):
1404+ try container.encode(x)
1405+ case .enumeration(let x):
1406+ try container.encode(x)
1407+ }
1408+ }
1409+}
1410+
1411+enum LabelOverlapEnum: String, Codable {
1412+ case parity = "parity"
1413+ case greedy = "greedy"
1414+}
1415+
1416+/// Font weight of the title. (e.g., `"bold"`).
1417+///
1418+/// The font weight of the legend title.
1419+enum TitleFontWeight: Codable {
1420+ case double(Double)
1421+ case string(String)
1422+
1423+ init(from decoder: Decoder) throws {
1424+ let container = try decoder.singleValueContainer()
1425+ if let x = try? container.decode(Double.self) {
1426+ self = .double(x)
1427+ return
1428+ }
1429+ if let x = try? container.decode(String.self) {
1430+ self = .string(x)
1431+ return
1432+ }
1433+ throw DecodingError.typeMismatch(TitleFontWeight.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for TitleFontWeight"))
1434+ }
1435+
1436+ func encode(to encoder: Encoder) throws {
1437+ var container = encoder.singleValueContainer()
1438+ switch self {
1439+ case .double(let x):
1440+ try container.encode(x)
1441+ case .string(let x):
1442+ try container.encode(x)
1443+ }
1444+ }
1445+}
1446+
1447+/// Specific axis config for axes with "band" scales.
1448+///
1449+/// Specific axis config for x-axis along the bottom edge of the chart.
1450+///
1451+/// Specific axis config for y-axis along the left edge of the chart.
1452+///
1453+/// Specific axis config for y-axis along the right edge of the chart.
1454+///
1455+/// Specific axis config for x-axis along the top edge of the chart.
1456+///
1457+/// X-axis specific config.
1458+///
1459+/// Y-axis specific config.
1460+// MARK: - VGAxisConfig
1461+struct VGAxisConfig: Codable {
1462+ /// An interpolation fraction indicating where, for `band` scales, axis ticks should be
1463+ /// positioned. A value of `0` places ticks at the left edge of their bands. A value of `0.5`
1464+ /// places ticks in the middle of their bands.
1465+ let bandPosition: Double?
1466+ /// A boolean flag indicating if the domain (the axis baseline) should be included as part of
1467+ /// the axis.
1468+ ///
1469+ /// __Default value:__ `true`
1470+ let domain: Bool?
1471+ /// Color of axis domain line.
1472+ ///
1473+ /// __Default value:__ (none, using Vega default).
1474+ let domainColor: String?
1475+ /// Stroke width of axis domain line
1476+ ///
1477+ /// __Default value:__ (none, using Vega default).
1478+ let domainWidth: Double?
1479+ /// A boolean flag indicating if grid lines should be included as part of the axis
1480+ ///
1481+ /// __Default value:__ `true` for [continuous scales](scale.html#continuous) that are not
1482+ /// binned; otherwise, `false`.
1483+ let grid: Bool?
1484+ /// Color of gridlines.
1485+ let gridColor: String?
1486+ /// The offset (in pixels) into which to begin drawing with the grid dash array.
1487+ let gridDash: [Double]?
1488+ /// The stroke opacity of grid (value between [0,1])
1489+ ///
1490+ /// __Default value:__ (`1` by default)
1491+ let gridOpacity: Double?
1492+ /// The grid width, in pixels.
1493+ let gridWidth: Double?
1494+ /// The rotation angle of the axis labels.
1495+ ///
1496+ /// __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
1497+ let labelAngle: Double?
1498+ /// Indicates if labels should be hidden if they exceed the axis range. If `false `(the
1499+ /// default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
1500+ /// they exceed the axis range by more than 1 pixel. If this property is a number, it
1501+ /// specifies the pixel tolerance: the maximum amount by which a label bounding box may
1502+ /// exceed the axis range.
1503+ ///
1504+ /// __Default value:__ `false`.
1505+ let labelBound: Label?
1506+ /// The color of the tick label, can be in hex color code or regular color name.
1507+ let labelColor: String?
1508+ /// Indicates if the first and last axis labels should be aligned flush with the scale range.
1509+ /// Flush alignment for a horizontal axis will left-align the first label and right-align the
1510+ /// last label. For vertical axes, bottom and top text baselines are applied instead. If this
1511+ /// property is a number, it also indicates the number of pixels by which to offset the first
1512+ /// and last labels; for example, a value of 2 will flush-align the first and last labels and
1513+ /// also push them 2 pixels outward from the center of the axis. The additional adjustment
1514+ /// can sometimes help the labels better visually group with corresponding axis ticks.
1515+ ///
1516+ /// __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
1517+ let labelFlush: Label?
1518+ /// The font of the tick label.
1519+ let labelFont: String?
1520+ /// The font size of the label, in pixels.
1521+ let labelFontSize: Double?
1522+ /// Maximum allowed pixel width of axis tick labels.
1523+ let labelLimit: Double?
1524+ /// The strategy to use for resolving overlap of axis labels. If `false` (the default), no
1525+ /// overlap reduction is attempted. If set to `true` or `"parity"`, a strategy of removing
1526+ /// every other label is used (this works well for standard linear axes). If set to
1527+ /// `"greedy"`, a linear scan of the labels is performed, removing any labels that overlaps
1528+ /// with the last visible label (this often works better for log-scaled axes).
1529+ ///
1530+ /// __Default value:__ `true` for non-nominal fields with non-log scales; `"greedy"` for log
1531+ /// scales; otherwise `false`.
1532+ let labelOverlap: LabelOverlapUnion?
1533+ /// The padding, in pixels, between axis and text labels.
1534+ let labelPadding: Double?
1535+ /// A boolean flag indicating if labels should be included as part of the axis.
1536+ ///
1537+ /// __Default value:__ `true`.
1538+ let labels: Bool?
1539+ /// The maximum extent in pixels that axis ticks and labels should use. This determines a
1540+ /// maximum offset value for axis titles.
1541+ ///
1542+ /// __Default value:__ `undefined`.
1543+ let maxExtent: Double?
1544+ /// The minimum extent in pixels that axis ticks and labels should use. This determines a
1545+ /// minimum offset value for axis titles.
1546+ ///
1547+ /// __Default value:__ `30` for y-axis; `undefined` for x-axis.
1548+ let minExtent: Double?
1549+ /// The color of the axis's tick.
1550+ let tickColor: String?
1551+ /// Boolean flag indicating if pixel position values should be rounded to the nearest integer.
1552+ let tickRound: Bool?
1553+ /// Boolean value that determines whether the axis should include ticks.
1554+ let ticks: Bool?
1555+ /// The size in pixels of axis ticks.
1556+ let tickSize: Double?
1557+ /// The width, in pixels, of ticks.
1558+ let tickWidth: Double?
1559+ /// Horizontal text alignment of axis titles.
1560+ let titleAlign: String?
1561+ /// Angle in degrees of axis titles.
1562+ let titleAngle: Double?
1563+ /// Vertical text baseline for axis titles.
1564+ let titleBaseline: String?
1565+ /// Color of the title, can be in hex color code or regular color name.
1566+ let titleColor: String?
1567+ /// Font of the title. (e.g., `"Helvetica Neue"`).
1568+ let titleFont: String?
1569+ /// Font size of the title.
1570+ let titleFontSize: Double?
1571+ /// Font weight of the title. (e.g., `"bold"`).
1572+ let titleFontWeight: TitleFontWeight?
1573+ /// Maximum allowed pixel width of axis titles.
1574+ let titleLimit: Double?
1575+ /// Max length for axis title if the title is automatically generated from the field's
1576+ /// description.
1577+ let titleMaxLength: Double?
1578+ /// The padding, in pixels, between title and axis.
1579+ let titlePadding: Double?
1580+ /// X-coordinate of the axis title relative to the axis group.
1581+ let titleX: Double?
1582+ /// Y-coordinate of the axis title relative to the axis group.
1583+ let titleY: Double?
1584+
1585+ enum CodingKeys: String, CodingKey {
1586+ case bandPosition = "bandPosition"
1587+ case domain = "domain"
1588+ case domainColor = "domainColor"
1589+ case domainWidth = "domainWidth"
1590+ case grid = "grid"
1591+ case gridColor = "gridColor"
1592+ case gridDash = "gridDash"
1593+ case gridOpacity = "gridOpacity"
1594+ case gridWidth = "gridWidth"
1595+ case labelAngle = "labelAngle"
1596+ case labelBound = "labelBound"
1597+ case labelColor = "labelColor"
1598+ case labelFlush = "labelFlush"
1599+ case labelFont = "labelFont"
1600+ case labelFontSize = "labelFontSize"
1601+ case labelLimit = "labelLimit"
1602+ case labelOverlap = "labelOverlap"
1603+ case labelPadding = "labelPadding"
1604+ case labels = "labels"
1605+ case maxExtent = "maxExtent"
1606+ case minExtent = "minExtent"
1607+ case tickColor = "tickColor"
1608+ case tickRound = "tickRound"
1609+ case ticks = "ticks"
1610+ case tickSize = "tickSize"
1611+ case tickWidth = "tickWidth"
1612+ case titleAlign = "titleAlign"
1613+ case titleAngle = "titleAngle"
1614+ case titleBaseline = "titleBaseline"
1615+ case titleColor = "titleColor"
1616+ case titleFont = "titleFont"
1617+ case titleFontSize = "titleFontSize"
1618+ case titleFontWeight = "titleFontWeight"
1619+ case titleLimit = "titleLimit"
1620+ case titleMaxLength = "titleMaxLength"
1621+ case titlePadding = "titlePadding"
1622+ case titleX = "titleX"
1623+ case titleY = "titleY"
1624+ }
1625+}
1626+
1627+// MARK: VGAxisConfig convenience initializers and mutators
1628+
1629+extension VGAxisConfig {
1630+ init(data: Data) throws {
1631+ self = try newJSONDecoder().decode(VGAxisConfig.self, from: data)
1632+ }
1633+
1634+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
1635+ guard let data = json.data(using: encoding) else {
1636+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
1637+ }
1638+ try self.init(data: data)
1639+ }
1640+
1641+ init(fromURL url: URL) throws {
1642+ try self.init(data: try Data(contentsOf: url))
1643+ }
1644+
1645+ func with(
1646+ bandPosition: Double?? = nil,
1647+ domain: Bool?? = nil,
1648+ domainColor: String?? = nil,
1649+ domainWidth: Double?? = nil,
1650+ grid: Bool?? = nil,
1651+ gridColor: String?? = nil,
1652+ gridDash: [Double]?? = nil,
1653+ gridOpacity: Double?? = nil,
1654+ gridWidth: Double?? = nil,
1655+ labelAngle: Double?? = nil,
1656+ labelBound: Label?? = nil,
1657+ labelColor: String?? = nil,
1658+ labelFlush: Label?? = nil,
1659+ labelFont: String?? = nil,
1660+ labelFontSize: Double?? = nil,
1661+ labelLimit: Double?? = nil,
1662+ labelOverlap: LabelOverlapUnion?? = nil,
1663+ labelPadding: Double?? = nil,
1664+ labels: Bool?? = nil,
1665+ maxExtent: Double?? = nil,
1666+ minExtent: Double?? = nil,
1667+ tickColor: String?? = nil,
1668+ tickRound: Bool?? = nil,
1669+ ticks: Bool?? = nil,
1670+ tickSize: Double?? = nil,
1671+ tickWidth: Double?? = nil,
1672+ titleAlign: String?? = nil,
1673+ titleAngle: Double?? = nil,
1674+ titleBaseline: String?? = nil,
1675+ titleColor: String?? = nil,
1676+ titleFont: String?? = nil,
1677+ titleFontSize: Double?? = nil,
1678+ titleFontWeight: TitleFontWeight?? = nil,
1679+ titleLimit: Double?? = nil,
1680+ titleMaxLength: Double?? = nil,
1681+ titlePadding: Double?? = nil,
1682+ titleX: Double?? = nil,
1683+ titleY: Double?? = nil
1684+ ) -> VGAxisConfig {
1685+ return VGAxisConfig(
1686+ bandPosition: bandPosition ?? self.bandPosition,
1687+ domain: domain ?? self.domain,
1688+ domainColor: domainColor ?? self.domainColor,
1689+ domainWidth: domainWidth ?? self.domainWidth,
1690+ grid: grid ?? self.grid,
1691+ gridColor: gridColor ?? self.gridColor,
1692+ gridDash: gridDash ?? self.gridDash,
1693+ gridOpacity: gridOpacity ?? self.gridOpacity,
1694+ gridWidth: gridWidth ?? self.gridWidth,
1695+ labelAngle: labelAngle ?? self.labelAngle,
1696+ labelBound: labelBound ?? self.labelBound,
1697+ labelColor: labelColor ?? self.labelColor,
1698+ labelFlush: labelFlush ?? self.labelFlush,
1699+ labelFont: labelFont ?? self.labelFont,
1700+ labelFontSize: labelFontSize ?? self.labelFontSize,
1701+ labelLimit: labelLimit ?? self.labelLimit,
1702+ labelOverlap: labelOverlap ?? self.labelOverlap,
1703+ labelPadding: labelPadding ?? self.labelPadding,
1704+ labels: labels ?? self.labels,
1705+ maxExtent: maxExtent ?? self.maxExtent,
1706+ minExtent: minExtent ?? self.minExtent,
1707+ tickColor: tickColor ?? self.tickColor,
1708+ tickRound: tickRound ?? self.tickRound,
1709+ ticks: ticks ?? self.ticks,
1710+ tickSize: tickSize ?? self.tickSize,
1711+ tickWidth: tickWidth ?? self.tickWidth,
1712+ titleAlign: titleAlign ?? self.titleAlign,
1713+ titleAngle: titleAngle ?? self.titleAngle,
1714+ titleBaseline: titleBaseline ?? self.titleBaseline,
1715+ titleColor: titleColor ?? self.titleColor,
1716+ titleFont: titleFont ?? self.titleFont,
1717+ titleFontSize: titleFontSize ?? self.titleFontSize,
1718+ titleFontWeight: titleFontWeight ?? self.titleFontWeight,
1719+ titleLimit: titleLimit ?? self.titleLimit,
1720+ titleMaxLength: titleMaxLength ?? self.titleMaxLength,
1721+ titlePadding: titlePadding ?? self.titlePadding,
1722+ titleX: titleX ?? self.titleX,
1723+ titleY: titleY ?? self.titleY
1724+ )
1725+ }
1726+
1727+ func jsonData() throws -> Data {
1728+ return try newJSONEncoder().encode(self)
1729+ }
1730+
1731+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
1732+ return String(data: try self.jsonData(), encoding: encoding)
1733+ }
1734+}
1735+
1736+/// Bar-Specific Config
1737+// MARK: - BarConfig
1738+struct BarConfig: Codable {
1739+ /// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
1740+ let align: HorizontalAlign?
1741+ /// The rotation angle of the text, in degrees.
1742+ let angle: Double?
1743+ /// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
1744+ ///
1745+ /// __Default value:__ `"middle"`
1746+ let baseline: VerticalAlign?
1747+ /// Offset between bar for binned field. Ideal value for this is either 0 (Preferred by
1748+ /// statisticians) or 1 (Vega-Lite Default, D3 example style).
1749+ ///
1750+ /// __Default value:__ `1`
1751+ let binSpacing: Double?
1752+ /// Default color. Note that `fill` and `stroke` have higher precedence than `color` and
1753+ /// will override `color`.
1754+ ///
1755+ /// __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
1756+ ///
1757+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
1758+ let color: String?
1759+ /// The default size of the bars on continuous scales.
1760+ ///
1761+ /// __Default value:__ `5`
1762+ let continuousBandSize: Double?
1763+ /// The mouse cursor used over the mark. Any valid [CSS cursor
1764+ /// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
1765+ let cursor: Cursor?
1766+ /// The size of the bars. If unspecified, the default size is `bandSize-1`,
1767+ /// which provides 1 pixel offset between bars.
1768+ let discreteBandSize: Double?
1769+ /// The horizontal offset, in pixels, between the text label and its anchor point. The offset
1770+ /// is applied after rotation by the _angle_ property.
1771+ let dx: Double?
1772+ /// The vertical offset, in pixels, between the text label and its anchor point. The offset
1773+ /// is applied after rotation by the _angle_ property.
1774+ let dy: Double?
1775+ /// Default Fill Color. This has higher precedence than config.color
1776+ ///
1777+ /// __Default value:__ (None)
1778+ let fill: String?
1779+ /// Whether the mark's color should be used as fill color instead of stroke color.
1780+ ///
1781+ /// __Default value:__ `true` for all marks except `point` and `false` for `point`.
1782+ ///
1783+ /// __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
1784+ ///
1785+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
1786+ let filled: Bool?
1787+ /// The fill opacity (value between [0,1]).
1788+ ///
1789+ /// __Default value:__ `1`
1790+ let fillOpacity: Double?
1791+ /// The typeface to set the text in (e.g., `"Helvetica Neue"`).
1792+ let font: String?
1793+ /// The font size, in pixels.
1794+ let fontSize: Double?
1795+ /// The font style (e.g., `"italic"`).
1796+ let fontStyle: FontStyle?
1797+ /// The font weight (e.g., `"bold"`).
1798+ let fontWeight: FontWeightUnion?
1799+ /// A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
1800+ let href: String?
1801+ /// The line interpolation method to use for line and area marks. One of the following:
1802+ /// - `"linear"`: piecewise linear segments, as in a polyline.
1803+ /// - `"linear-closed"`: close the linear segments to form a polygon.
1804+ /// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
1805+ /// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
1806+ /// function.
1807+ /// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
1808+ /// function.
1809+ /// - `"basis"`: a B-spline, with control point duplication on the ends.
1810+ /// - `"basis-open"`: an open B-spline; may not intersect the start or end.
1811+ /// - `"basis-closed"`: a closed B-spline, as in a loop.
1812+ /// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
1813+ /// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
1814+ /// will intersect other control points.
1815+ /// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
1816+ /// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
1817+ /// spline.
1818+ /// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
1819+ let interpolate: Interpolate?
1820+ /// The maximum length of the text mark in pixels (default 0, indicating no limit). The text
1821+ /// value will be automatically truncated if the rendered size exceeds the limit.
1822+ let limit: Double?
1823+ /// The overall opacity (value between [0,1]).
1824+ ///
1825+ /// __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
1826+ /// `square` marks or layered `bar` charts and `1` otherwise.
1827+ let opacity: Double?
1828+ /// The orientation of a non-stacked bar, tick, area, and line charts.
1829+ /// The value is either horizontal (default) or vertical.
1830+ /// - For bar, rule and tick, this determines whether the size of the bar and tick
1831+ /// should be applied to x or y dimension.
1832+ /// - For area, this property determines the orient property of the Vega output.
1833+ /// - For line, this property determines the sort order of the points in the line
1834+ /// if `config.sortLineBy` is not specified.
1835+ /// For stacked charts, this is always determined by the orientation of the stack;
1836+ /// therefore explicitly specified value will be ignored.
1837+ let orient: Orient?
1838+ /// Polar coordinate radial offset, in pixels, of the text label from the origin determined
1839+ /// by the `x` and `y` properties.
1840+ let radius: Double?
1841+ /// The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
1842+ /// `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
1843+ ///
1844+ /// __Default value:__ `"circle"`
1845+ let shape: String?
1846+ /// The pixel area each the point/circle/square.
1847+ /// For example: in the case of circles, the radius is determined in part by the square root
1848+ /// of the size value.
1849+ ///
1850+ /// __Default value:__ `30`
1851+ let size: Double?
1852+ /// Default Stroke Color. This has higher precedence than config.color
1853+ ///
1854+ /// __Default value:__ (None)
1855+ let stroke: String?
1856+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
1857+ let strokeDash: [Double]?
1858+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
1859+ let strokeDashOffset: Double?
1860+ /// The stroke opacity (value between [0,1]).
1861+ ///
1862+ /// __Default value:__ `1`
1863+ let strokeOpacity: Double?
1864+ /// The stroke width, in pixels.
1865+ let strokeWidth: Double?
1866+ /// Depending on the interpolation type, sets the tension parameter (for line and area marks).
1867+ let tension: Double?
1868+ /// Placeholder text if the `text` channel is not specified
1869+ let text: String?
1870+ /// Polar coordinate angle, in radians, of the text label from the origin determined by the
1871+ /// `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
1872+ /// `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
1873+ /// indicating "north".
1874+ let theta: Double?
1875+
1876+ enum CodingKeys: String, CodingKey {
1877+ case align = "align"
1878+ case angle = "angle"
1879+ case baseline = "baseline"
1880+ case binSpacing = "binSpacing"
1881+ case color = "color"
1882+ case continuousBandSize = "continuousBandSize"
1883+ case cursor = "cursor"
1884+ case discreteBandSize = "discreteBandSize"
1885+ case dx = "dx"
1886+ case dy = "dy"
1887+ case fill = "fill"
1888+ case filled = "filled"
1889+ case fillOpacity = "fillOpacity"
1890+ case font = "font"
1891+ case fontSize = "fontSize"
1892+ case fontStyle = "fontStyle"
1893+ case fontWeight = "fontWeight"
1894+ case href = "href"
1895+ case interpolate = "interpolate"
1896+ case limit = "limit"
1897+ case opacity = "opacity"
1898+ case orient = "orient"
1899+ case radius = "radius"
1900+ case shape = "shape"
1901+ case size = "size"
1902+ case stroke = "stroke"
1903+ case strokeDash = "strokeDash"
1904+ case strokeDashOffset = "strokeDashOffset"
1905+ case strokeOpacity = "strokeOpacity"
1906+ case strokeWidth = "strokeWidth"
1907+ case tension = "tension"
1908+ case text = "text"
1909+ case theta = "theta"
1910+ }
1911+}
1912+
1913+// MARK: BarConfig convenience initializers and mutators
1914+
1915+extension BarConfig {
1916+ init(data: Data) throws {
1917+ self = try newJSONDecoder().decode(BarConfig.self, from: data)
1918+ }
1919+
1920+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
1921+ guard let data = json.data(using: encoding) else {
1922+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
1923+ }
1924+ try self.init(data: data)
1925+ }
1926+
1927+ init(fromURL url: URL) throws {
1928+ try self.init(data: try Data(contentsOf: url))
1929+ }
1930+
1931+ func with(
1932+ align: HorizontalAlign?? = nil,
1933+ angle: Double?? = nil,
1934+ baseline: VerticalAlign?? = nil,
1935+ binSpacing: Double?? = nil,
1936+ color: String?? = nil,
1937+ continuousBandSize: Double?? = nil,
1938+ cursor: Cursor?? = nil,
1939+ discreteBandSize: Double?? = nil,
1940+ dx: Double?? = nil,
1941+ dy: Double?? = nil,
1942+ fill: String?? = nil,
1943+ filled: Bool?? = nil,
1944+ fillOpacity: Double?? = nil,
1945+ font: String?? = nil,
1946+ fontSize: Double?? = nil,
1947+ fontStyle: FontStyle?? = nil,
1948+ fontWeight: FontWeightUnion?? = nil,
1949+ href: String?? = nil,
1950+ interpolate: Interpolate?? = nil,
1951+ limit: Double?? = nil,
1952+ opacity: Double?? = nil,
1953+ orient: Orient?? = nil,
1954+ radius: Double?? = nil,
1955+ shape: String?? = nil,
1956+ size: Double?? = nil,
1957+ stroke: String?? = nil,
1958+ strokeDash: [Double]?? = nil,
1959+ strokeDashOffset: Double?? = nil,
1960+ strokeOpacity: Double?? = nil,
1961+ strokeWidth: Double?? = nil,
1962+ tension: Double?? = nil,
1963+ text: String?? = nil,
1964+ theta: Double?? = nil
1965+ ) -> BarConfig {
1966+ return BarConfig(
1967+ align: align ?? self.align,
1968+ angle: angle ?? self.angle,
1969+ baseline: baseline ?? self.baseline,
1970+ binSpacing: binSpacing ?? self.binSpacing,
1971+ color: color ?? self.color,
1972+ continuousBandSize: continuousBandSize ?? self.continuousBandSize,
1973+ cursor: cursor ?? self.cursor,
1974+ discreteBandSize: discreteBandSize ?? self.discreteBandSize,
1975+ dx: dx ?? self.dx,
1976+ dy: dy ?? self.dy,
1977+ fill: fill ?? self.fill,
1978+ filled: filled ?? self.filled,
1979+ fillOpacity: fillOpacity ?? self.fillOpacity,
1980+ font: font ?? self.font,
1981+ fontSize: fontSize ?? self.fontSize,
1982+ fontStyle: fontStyle ?? self.fontStyle,
1983+ fontWeight: fontWeight ?? self.fontWeight,
1984+ href: href ?? self.href,
1985+ interpolate: interpolate ?? self.interpolate,
1986+ limit: limit ?? self.limit,
1987+ opacity: opacity ?? self.opacity,
1988+ orient: orient ?? self.orient,
1989+ radius: radius ?? self.radius,
1990+ shape: shape ?? self.shape,
1991+ size: size ?? self.size,
1992+ stroke: stroke ?? self.stroke,
1993+ strokeDash: strokeDash ?? self.strokeDash,
1994+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
1995+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
1996+ strokeWidth: strokeWidth ?? self.strokeWidth,
1997+ tension: tension ?? self.tension,
1998+ text: text ?? self.text,
1999+ theta: theta ?? self.theta
2000+ )
2001+ }
2002+
2003+ func jsonData() throws -> Data {
2004+ return try newJSONEncoder().encode(self)
2005+ }
2006+
2007+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2008+ return String(data: try self.jsonData(), encoding: encoding)
2009+ }
2010+}
2011+
2012+/// Defines how Vega-Lite generates title for fields. There are three possible styles:
2013+///
2014+/// - `"verbal"` (Default) - displays function in a verbal style (e.g., "Sum of field",
2015+/// "Year-month of date", "field (binned)").
2016+/// - `"function"` - displays function using parentheses and capitalized texts (e.g.,
2017+/// "SUM(field)", "YEARMONTH(date)", "BIN(field)").
2018+/// - `"plain"` - displays only the field name without functions (e.g., "field", "date",
2019+/// "field").
2020+enum FieldTitle: String, Codable {
2021+ case verbal = "verbal"
2022+ case functional = "functional"
2023+ case plain = "plain"
2024+}
2025+
2026+/// Defines how Vega-Lite should handle invalid values (`null` and `NaN`).
2027+/// - If set to `"filter"` (default), all data items with null values are filtered.
2028+/// - If `null`, all data items are included. In this case, invalid values will be
2029+/// interpreted as zeroes.
2030+enum InvalidValues: String, Codable {
2031+ case filter = "filter"
2032+}
2033+
2034+/// Legend configuration, which determines default properties for all [legends](legend.html).
2035+/// For a full list of legend configuration options, please see the [corresponding section of
2036+/// in the legend documentation](legend.html#config).
2037+// MARK: - LegendConfig
2038+struct LegendConfig: Codable {
2039+ /// Corner radius for the full legend.
2040+ let cornerRadius: Double?
2041+ /// Padding (in pixels) between legend entries in a symbol legend.
2042+ let entryPadding: Double?
2043+ /// Background fill color for the full legend.
2044+ let fillColor: String?
2045+ /// The height of the gradient, in pixels.
2046+ let gradientHeight: Double?
2047+ /// Text baseline for color ramp gradient labels.
2048+ let gradientLabelBaseline: String?
2049+ /// The maximum allowed length in pixels of color ramp gradient labels.
2050+ let gradientLabelLimit: Double?
2051+ /// Vertical offset in pixels for color ramp gradient labels.
2052+ let gradientLabelOffset: Double?
2053+ /// The color of the gradient stroke, can be in hex color code or regular color name.
2054+ let gradientStrokeColor: String?
2055+ /// The width of the gradient stroke, in pixels.
2056+ let gradientStrokeWidth: Double?
2057+ /// The width of the gradient, in pixels.
2058+ let gradientWidth: Double?
2059+ /// The alignment of the legend label, can be left, middle or right.
2060+ let labelAlign: String?
2061+ /// The position of the baseline of legend label, can be top, middle or bottom.
2062+ let labelBaseline: String?
2063+ /// The color of the legend label, can be in hex color code or regular color name.
2064+ let labelColor: String?
2065+ /// The font of the legend label.
2066+ let labelFont: String?
2067+ /// The font size of legend label.
2068+ ///
2069+ /// __Default value:__ `10`.
2070+ let labelFontSize: Double?
2071+ /// Maximum allowed pixel width of axis tick labels.
2072+ let labelLimit: Double?
2073+ /// The offset of the legend label.
2074+ let labelOffset: Double?
2075+ /// The offset, in pixels, by which to displace the legend from the edge of the enclosing
2076+ /// group or data rectangle.
2077+ ///
2078+ /// __Default value:__ `0`
2079+ let offset: Double?
2080+ /// The orientation of the legend, which determines how the legend is positioned within the
2081+ /// scene. One of "left", "right", "top-left", "top-right", "bottom-left", "bottom-right",
2082+ /// "none".
2083+ ///
2084+ /// __Default value:__ `"right"`
2085+ let orient: LegendOrient?
2086+ /// The padding, in pixels, between the legend and axis.
2087+ let padding: Double?
2088+ /// Whether month names and weekday names should be abbreviated.
2089+ ///
2090+ /// __Default value:__ `false`
2091+ let shortTimeLabels: Bool?
2092+ /// Border stroke color for the full legend.
2093+ let strokeColor: String?
2094+ /// Border stroke dash pattern for the full legend.
2095+ let strokeDash: [Double]?
2096+ /// Border stroke width for the full legend.
2097+ let strokeWidth: Double?
2098+ /// The color of the legend symbol,
2099+ let symbolColor: String?
2100+ /// The size of the legend symbol, in pixels.
2101+ let symbolSize: Double?
2102+ /// The width of the symbol's stroke.
2103+ let symbolStrokeWidth: Double?
2104+ /// Default shape type (such as "circle") for legend symbols.
2105+ let symbolType: String?
2106+ /// Horizontal text alignment for legend titles.
2107+ let titleAlign: String?
2108+ /// Vertical text baseline for legend titles.
2109+ let titleBaseline: String?
2110+ /// The color of the legend title, can be in hex color code or regular color name.
2111+ let titleColor: String?
2112+ /// The font of the legend title.
2113+ let titleFont: String?
2114+ /// The font size of the legend title.
2115+ let titleFontSize: Double?
2116+ /// The font weight of the legend title.
2117+ let titleFontWeight: TitleFontWeight?
2118+ /// Maximum allowed pixel width of axis titles.
2119+ let titleLimit: Double?
2120+ /// The padding, in pixels, between title and legend.
2121+ let titlePadding: Double?
2122+
2123+ enum CodingKeys: String, CodingKey {
2124+ case cornerRadius = "cornerRadius"
2125+ case entryPadding = "entryPadding"
2126+ case fillColor = "fillColor"
2127+ case gradientHeight = "gradientHeight"
2128+ case gradientLabelBaseline = "gradientLabelBaseline"
2129+ case gradientLabelLimit = "gradientLabelLimit"
2130+ case gradientLabelOffset = "gradientLabelOffset"
2131+ case gradientStrokeColor = "gradientStrokeColor"
2132+ case gradientStrokeWidth = "gradientStrokeWidth"
2133+ case gradientWidth = "gradientWidth"
2134+ case labelAlign = "labelAlign"
2135+ case labelBaseline = "labelBaseline"
2136+ case labelColor = "labelColor"
2137+ case labelFont = "labelFont"
2138+ case labelFontSize = "labelFontSize"
2139+ case labelLimit = "labelLimit"
2140+ case labelOffset = "labelOffset"
2141+ case offset = "offset"
2142+ case orient = "orient"
2143+ case padding = "padding"
2144+ case shortTimeLabels = "shortTimeLabels"
2145+ case strokeColor = "strokeColor"
2146+ case strokeDash = "strokeDash"
2147+ case strokeWidth = "strokeWidth"
2148+ case symbolColor = "symbolColor"
2149+ case symbolSize = "symbolSize"
2150+ case symbolStrokeWidth = "symbolStrokeWidth"
2151+ case symbolType = "symbolType"
2152+ case titleAlign = "titleAlign"
2153+ case titleBaseline = "titleBaseline"
2154+ case titleColor = "titleColor"
2155+ case titleFont = "titleFont"
2156+ case titleFontSize = "titleFontSize"
2157+ case titleFontWeight = "titleFontWeight"
2158+ case titleLimit = "titleLimit"
2159+ case titlePadding = "titlePadding"
2160+ }
2161+}
2162+
2163+// MARK: LegendConfig convenience initializers and mutators
2164+
2165+extension LegendConfig {
2166+ init(data: Data) throws {
2167+ self = try newJSONDecoder().decode(LegendConfig.self, from: data)
2168+ }
2169+
2170+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2171+ guard let data = json.data(using: encoding) else {
2172+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2173+ }
2174+ try self.init(data: data)
2175+ }
2176+
2177+ init(fromURL url: URL) throws {
2178+ try self.init(data: try Data(contentsOf: url))
2179+ }
2180+
2181+ func with(
2182+ cornerRadius: Double?? = nil,
2183+ entryPadding: Double?? = nil,
2184+ fillColor: String?? = nil,
2185+ gradientHeight: Double?? = nil,
2186+ gradientLabelBaseline: String?? = nil,
2187+ gradientLabelLimit: Double?? = nil,
2188+ gradientLabelOffset: Double?? = nil,
2189+ gradientStrokeColor: String?? = nil,
2190+ gradientStrokeWidth: Double?? = nil,
2191+ gradientWidth: Double?? = nil,
2192+ labelAlign: String?? = nil,
2193+ labelBaseline: String?? = nil,
2194+ labelColor: String?? = nil,
2195+ labelFont: String?? = nil,
2196+ labelFontSize: Double?? = nil,
2197+ labelLimit: Double?? = nil,
2198+ labelOffset: Double?? = nil,
2199+ offset: Double?? = nil,
2200+ orient: LegendOrient?? = nil,
2201+ padding: Double?? = nil,
2202+ shortTimeLabels: Bool?? = nil,
2203+ strokeColor: String?? = nil,
2204+ strokeDash: [Double]?? = nil,
2205+ strokeWidth: Double?? = nil,
2206+ symbolColor: String?? = nil,
2207+ symbolSize: Double?? = nil,
2208+ symbolStrokeWidth: Double?? = nil,
2209+ symbolType: String?? = nil,
2210+ titleAlign: String?? = nil,
2211+ titleBaseline: String?? = nil,
2212+ titleColor: String?? = nil,
2213+ titleFont: String?? = nil,
2214+ titleFontSize: Double?? = nil,
2215+ titleFontWeight: TitleFontWeight?? = nil,
2216+ titleLimit: Double?? = nil,
2217+ titlePadding: Double?? = nil
2218+ ) -> LegendConfig {
2219+ return LegendConfig(
2220+ cornerRadius: cornerRadius ?? self.cornerRadius,
2221+ entryPadding: entryPadding ?? self.entryPadding,
2222+ fillColor: fillColor ?? self.fillColor,
2223+ gradientHeight: gradientHeight ?? self.gradientHeight,
2224+ gradientLabelBaseline: gradientLabelBaseline ?? self.gradientLabelBaseline,
2225+ gradientLabelLimit: gradientLabelLimit ?? self.gradientLabelLimit,
2226+ gradientLabelOffset: gradientLabelOffset ?? self.gradientLabelOffset,
2227+ gradientStrokeColor: gradientStrokeColor ?? self.gradientStrokeColor,
2228+ gradientStrokeWidth: gradientStrokeWidth ?? self.gradientStrokeWidth,
2229+ gradientWidth: gradientWidth ?? self.gradientWidth,
2230+ labelAlign: labelAlign ?? self.labelAlign,
2231+ labelBaseline: labelBaseline ?? self.labelBaseline,
2232+ labelColor: labelColor ?? self.labelColor,
2233+ labelFont: labelFont ?? self.labelFont,
2234+ labelFontSize: labelFontSize ?? self.labelFontSize,
2235+ labelLimit: labelLimit ?? self.labelLimit,
2236+ labelOffset: labelOffset ?? self.labelOffset,
2237+ offset: offset ?? self.offset,
2238+ orient: orient ?? self.orient,
2239+ padding: padding ?? self.padding,
2240+ shortTimeLabels: shortTimeLabels ?? self.shortTimeLabels,
2241+ strokeColor: strokeColor ?? self.strokeColor,
2242+ strokeDash: strokeDash ?? self.strokeDash,
2243+ strokeWidth: strokeWidth ?? self.strokeWidth,
2244+ symbolColor: symbolColor ?? self.symbolColor,
2245+ symbolSize: symbolSize ?? self.symbolSize,
2246+ symbolStrokeWidth: symbolStrokeWidth ?? self.symbolStrokeWidth,
2247+ symbolType: symbolType ?? self.symbolType,
2248+ titleAlign: titleAlign ?? self.titleAlign,
2249+ titleBaseline: titleBaseline ?? self.titleBaseline,
2250+ titleColor: titleColor ?? self.titleColor,
2251+ titleFont: titleFont ?? self.titleFont,
2252+ titleFontSize: titleFontSize ?? self.titleFontSize,
2253+ titleFontWeight: titleFontWeight ?? self.titleFontWeight,
2254+ titleLimit: titleLimit ?? self.titleLimit,
2255+ titlePadding: titlePadding ?? self.titlePadding
2256+ )
2257+ }
2258+
2259+ func jsonData() throws -> Data {
2260+ return try newJSONEncoder().encode(self)
2261+ }
2262+
2263+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2264+ return String(data: try self.jsonData(), encoding: encoding)
2265+ }
2266+}
2267+
2268+/// The orientation of the legend, which determines how the legend is positioned within the
2269+/// scene. One of "left", "right", "top-left", "top-right", "bottom-left", "bottom-right",
2270+/// "none".
2271+///
2272+/// __Default value:__ `"right"`
2273+enum LegendOrient: String, Codable {
2274+ case legendOrientLeft = "left"
2275+ case legendOrientRight = "right"
2276+ case topLeft = "top-left"
2277+ case topRight = "top-right"
2278+ case bottomLeft = "bottom-left"
2279+ case bottomRight = "bottom-right"
2280+ case none = "none"
2281+}
2282+
2283+/// The default visualization padding, in pixels, from the edge of the visualization canvas
2284+/// to the data rectangle. If a number, specifies padding for all sides.
2285+/// If an object, the value should have the format `{"left": 5, "top": 5, "right": 5,
2286+/// "bottom": 5}` to specify padding for each side of the visualization.
2287+///
2288+/// __Default value__: `5`
2289+enum Padding: Codable {
2290+ case double(Double)
2291+ case paddingClass(PaddingClass)
2292+
2293+ init(from decoder: Decoder) throws {
2294+ let container = try decoder.singleValueContainer()
2295+ if let x = try? container.decode(Double.self) {
2296+ self = .double(x)
2297+ return
2298+ }
2299+ if let x = try? container.decode(PaddingClass.self) {
2300+ self = .paddingClass(x)
2301+ return
2302+ }
2303+ throw DecodingError.typeMismatch(Padding.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Padding"))
2304+ }
2305+
2306+ func encode(to encoder: Encoder) throws {
2307+ var container = encoder.singleValueContainer()
2308+ switch self {
2309+ case .double(let x):
2310+ try container.encode(x)
2311+ case .paddingClass(let x):
2312+ try container.encode(x)
2313+ }
2314+ }
2315+}
2316+
2317+// MARK: - PaddingClass
2318+struct PaddingClass: Codable {
2319+ let bottom: Double?
2320+ let paddingLeft: Double?
2321+ let paddingRight: Double?
2322+ let top: Double?
2323+
2324+ enum CodingKeys: String, CodingKey {
2325+ case bottom = "bottom"
2326+ case paddingLeft = "left"
2327+ case paddingRight = "right"
2328+ case top = "top"
2329+ }
2330+}
2331+
2332+// MARK: PaddingClass convenience initializers and mutators
2333+
2334+extension PaddingClass {
2335+ init(data: Data) throws {
2336+ self = try newJSONDecoder().decode(PaddingClass.self, from: data)
2337+ }
2338+
2339+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2340+ guard let data = json.data(using: encoding) else {
2341+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2342+ }
2343+ try self.init(data: data)
2344+ }
2345+
2346+ init(fromURL url: URL) throws {
2347+ try self.init(data: try Data(contentsOf: url))
2348+ }
2349+
2350+ func with(
2351+ bottom: Double?? = nil,
2352+ paddingLeft: Double?? = nil,
2353+ paddingRight: Double?? = nil,
2354+ top: Double?? = nil
2355+ ) -> PaddingClass {
2356+ return PaddingClass(
2357+ bottom: bottom ?? self.bottom,
2358+ paddingLeft: paddingLeft ?? self.paddingLeft,
2359+ paddingRight: paddingRight ?? self.paddingRight,
2360+ top: top ?? self.top
2361+ )
2362+ }
2363+
2364+ func jsonData() throws -> Data {
2365+ return try newJSONEncoder().encode(self)
2366+ }
2367+
2368+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2369+ return String(data: try self.jsonData(), encoding: encoding)
2370+ }
2371+}
2372+
2373+/// Projection configuration, which determines default properties for all
2374+/// [projections](projection.html). For a full list of projection configuration options,
2375+/// please see the [corresponding section of the projection
2376+/// documentation](projection.html#config).
2377+///
2378+/// Any property of Projection can be in config
2379+// MARK: - ProjectionConfig
2380+struct ProjectionConfig: Codable {
2381+ /// Sets the projection’s center to the specified center, a two-element array of longitude
2382+ /// and latitude in degrees.
2383+ ///
2384+ /// __Default value:__ `[0, 0]`
2385+ let center: [Double]?
2386+ /// Sets the projection’s clipping circle radius to the specified angle in degrees. If
2387+ /// `null`, switches to [antimeridian](http://bl.ocks.org/mbostock/3788999) cutting rather
2388+ /// than small-circle clipping.
2389+ let clipAngle: Double?
2390+ /// Sets the projection’s viewport clip extent to the specified bounds in pixels. The extent
2391+ /// bounds are specified as an array `[[x0, y0], [x1, y1]]`, where `x0` is the left-side of
2392+ /// the viewport, `y0` is the top, `x1` is the right and `y1` is the bottom. If `null`, no
2393+ /// viewport clipping is performed.
2394+ let clipExtent: [[Double]]?
2395+ let coefficient: Double?
2396+ let distance: Double?
2397+ let fraction: Double?
2398+ let lobes: Double?
2399+ let parallel: Double?
2400+ /// Sets the threshold for the projection’s [adaptive
2401+ /// resampling](http://bl.ocks.org/mbostock/3795544) to the specified value in pixels. This
2402+ /// value corresponds to the [Douglas–Peucker
2403+ /// distance](http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm).
2404+ /// If precision is not specified, returns the projection’s current resampling precision
2405+ /// which defaults to `√0.5 ≅ 0.70710…`.
2406+ let precision: [String: TitleFontWeight]?
2407+ let radius: Double?
2408+ let ratio: Double?
2409+ /// Sets the projection’s three-axis rotation to the specified angles, which must be a two-
2410+ /// or three-element array of numbers [`lambda`, `phi`, `gamma`] specifying the rotation
2411+ /// angles in degrees about each spherical axis. (These correspond to yaw, pitch and roll.)
2412+ ///
2413+ /// __Default value:__ `[0, 0, 0]`
2414+ let rotate: [Double]?
2415+ let spacing: Double?
2416+ let tilt: Double?
2417+ /// The cartographic projection to use. This value is case-insensitive, for example
2418+ /// `"albers"` and `"Albers"` indicate the same projection type. You can find all valid
2419+ /// projection types [in the
2420+ /// documentation](https://vega.github.io/vega-lite/docs/projection.html#projection-types).
2421+ ///
2422+ /// __Default value:__ `mercator`
2423+ let type: VGProjectionType?
2424+
2425+ enum CodingKeys: String, CodingKey {
2426+ case center = "center"
2427+ case clipAngle = "clipAngle"
2428+ case clipExtent = "clipExtent"
2429+ case coefficient = "coefficient"
2430+ case distance = "distance"
2431+ case fraction = "fraction"
2432+ case lobes = "lobes"
2433+ case parallel = "parallel"
2434+ case precision = "precision"
2435+ case radius = "radius"
2436+ case ratio = "ratio"
2437+ case rotate = "rotate"
2438+ case spacing = "spacing"
2439+ case tilt = "tilt"
2440+ case type = "type"
2441+ }
2442+}
2443+
2444+// MARK: ProjectionConfig convenience initializers and mutators
2445+
2446+extension ProjectionConfig {
2447+ init(data: Data) throws {
2448+ self = try newJSONDecoder().decode(ProjectionConfig.self, from: data)
2449+ }
2450+
2451+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2452+ guard let data = json.data(using: encoding) else {
2453+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2454+ }
2455+ try self.init(data: data)
2456+ }
2457+
2458+ init(fromURL url: URL) throws {
2459+ try self.init(data: try Data(contentsOf: url))
2460+ }
2461+
2462+ func with(
2463+ center: [Double]?? = nil,
2464+ clipAngle: Double?? = nil,
2465+ clipExtent: [[Double]]?? = nil,
2466+ coefficient: Double?? = nil,
2467+ distance: Double?? = nil,
2468+ fraction: Double?? = nil,
2469+ lobes: Double?? = nil,
2470+ parallel: Double?? = nil,
2471+ precision: [String: TitleFontWeight]?? = nil,
2472+ radius: Double?? = nil,
2473+ ratio: Double?? = nil,
2474+ rotate: [Double]?? = nil,
2475+ spacing: Double?? = nil,
2476+ tilt: Double?? = nil,
2477+ type: VGProjectionType?? = nil
2478+ ) -> ProjectionConfig {
2479+ return ProjectionConfig(
2480+ center: center ?? self.center,
2481+ clipAngle: clipAngle ?? self.clipAngle,
2482+ clipExtent: clipExtent ?? self.clipExtent,
2483+ coefficient: coefficient ?? self.coefficient,
2484+ distance: distance ?? self.distance,
2485+ fraction: fraction ?? self.fraction,
2486+ lobes: lobes ?? self.lobes,
2487+ parallel: parallel ?? self.parallel,
2488+ precision: precision ?? self.precision,
2489+ radius: radius ?? self.radius,
2490+ ratio: ratio ?? self.ratio,
2491+ rotate: rotate ?? self.rotate,
2492+ spacing: spacing ?? self.spacing,
2493+ tilt: tilt ?? self.tilt,
2494+ type: type ?? self.type
2495+ )
2496+ }
2497+
2498+ func jsonData() throws -> Data {
2499+ return try newJSONEncoder().encode(self)
2500+ }
2501+
2502+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2503+ return String(data: try self.jsonData(), encoding: encoding)
2504+ }
2505+}
2506+
2507+/// The cartographic projection to use. This value is case-insensitive, for example
2508+/// `"albers"` and `"Albers"` indicate the same projection type. You can find all valid
2509+/// projection types [in the
2510+/// documentation](https://vega.github.io/vega-lite/docs/projection.html#projection-types).
2511+///
2512+/// __Default value:__ `mercator`
2513+enum VGProjectionType: String, Codable {
2514+ case albers = "albers"
2515+ case albersUsa = "albersUsa"
2516+ case azimuthalEqualArea = "azimuthalEqualArea"
2517+ case azimuthalEquidistant = "azimuthalEquidistant"
2518+ case conicConformal = "conicConformal"
2519+ case conicEqualArea = "conicEqualArea"
2520+ case conicEquidistant = "conicEquidistant"
2521+ case equirectangular = "equirectangular"
2522+ case gnomonic = "gnomonic"
2523+ case mercator = "mercator"
2524+ case orthographic = "orthographic"
2525+ case stereographic = "stereographic"
2526+ case transverseMercator = "transverseMercator"
2527+}
2528+
2529+enum RangeConfigValue: Codable {
2530+ case unionArray([TitleFontWeight])
2531+ case vgScheme(VGScheme)
2532+
2533+ init(from decoder: Decoder) throws {
2534+ let container = try decoder.singleValueContainer()
2535+ if let x = try? container.decode([TitleFontWeight].self) {
2536+ self = .unionArray(x)
2537+ return
2538+ }
2539+ if let x = try? container.decode(VGScheme.self) {
2540+ self = .vgScheme(x)
2541+ return
2542+ }
2543+ throw DecodingError.typeMismatch(RangeConfigValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for RangeConfigValue"))
2544+ }
2545+
2546+ func encode(to encoder: Encoder) throws {
2547+ var container = encoder.singleValueContainer()
2548+ switch self {
2549+ case .unionArray(let x):
2550+ try container.encode(x)
2551+ case .vgScheme(let x):
2552+ try container.encode(x)
2553+ }
2554+ }
2555+}
2556+
2557+// MARK: - VGScheme
2558+struct VGScheme: Codable {
2559+ let count: Double?
2560+ let extent: [Double]?
2561+ let scheme: String?
2562+ let step: Double?
2563+
2564+ enum CodingKeys: String, CodingKey {
2565+ case count = "count"
2566+ case extent = "extent"
2567+ case scheme = "scheme"
2568+ case step = "step"
2569+ }
2570+}
2571+
2572+// MARK: VGScheme convenience initializers and mutators
2573+
2574+extension VGScheme {
2575+ init(data: Data) throws {
2576+ self = try newJSONDecoder().decode(VGScheme.self, from: data)
2577+ }
2578+
2579+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2580+ guard let data = json.data(using: encoding) else {
2581+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2582+ }
2583+ try self.init(data: data)
2584+ }
2585+
2586+ init(fromURL url: URL) throws {
2587+ try self.init(data: try Data(contentsOf: url))
2588+ }
2589+
2590+ func with(
2591+ count: Double?? = nil,
2592+ extent: [Double]?? = nil,
2593+ scheme: String?? = nil,
2594+ step: Double?? = nil
2595+ ) -> VGScheme {
2596+ return VGScheme(
2597+ count: count ?? self.count,
2598+ extent: extent ?? self.extent,
2599+ scheme: scheme ?? self.scheme,
2600+ step: step ?? self.step
2601+ )
2602+ }
2603+
2604+ func jsonData() throws -> Data {
2605+ return try newJSONEncoder().encode(self)
2606+ }
2607+
2608+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2609+ return String(data: try self.jsonData(), encoding: encoding)
2610+ }
2611+}
2612+
2613+/// Scale configuration determines default properties for all [scales](scale.html). For a
2614+/// full list of scale configuration options, please see the [corresponding section of the
2615+/// scale documentation](scale.html#config).
2616+// MARK: - ScaleConfig
2617+struct ScaleConfig: Codable {
2618+ /// Default inner padding for `x` and `y` band-ordinal scales.
2619+ ///
2620+ /// __Default value:__ `0.1`
2621+ let bandPaddingInner: Double?
2622+ /// Default outer padding for `x` and `y` band-ordinal scales.
2623+ /// If not specified, by default, band scale's paddingOuter is paddingInner/2.
2624+ let bandPaddingOuter: Double?
2625+ /// If true, values that exceed the data domain are clamped to either the minimum or maximum
2626+ /// range value
2627+ let clamp: Bool?
2628+ /// Default padding for continuous scales.
2629+ ///
2630+ /// __Default:__ `5` for continuous x-scale of a vertical bar and continuous y-scale of a
2631+ /// horizontal bar.; `0` otherwise.
2632+ let continuousPadding: Double?
2633+ /// The default max value for mapping quantitative fields to bar's size/bandSize.
2634+ ///
2635+ /// If undefined (default), we will use the scale's `rangeStep` - 1.
2636+ let maxBandSize: Double?
2637+ /// The default max value for mapping quantitative fields to text's size/fontSize.
2638+ ///
2639+ /// __Default value:__ `40`
2640+ let maxFontSize: Double?
2641+ /// Default max opacity for mapping a field to opacity.
2642+ ///
2643+ /// __Default value:__ `0.8`
2644+ let maxOpacity: Double?
2645+ /// Default max value for point size scale.
2646+ let maxSize: Double?
2647+ /// Default max strokeWidth for strokeWidth (or rule/line's size) scale.
2648+ ///
2649+ /// __Default value:__ `4`
2650+ let maxStrokeWidth: Double?
2651+ /// The default min value for mapping quantitative fields to bar and tick's size/bandSize
2652+ /// scale with zero=false.
2653+ ///
2654+ /// __Default value:__ `2`
2655+ let minBandSize: Double?
2656+ /// The default min value for mapping quantitative fields to tick's size/fontSize scale with
2657+ /// zero=false
2658+ ///
2659+ /// __Default value:__ `8`
2660+ let minFontSize: Double?
2661+ /// Default minimum opacity for mapping a field to opacity.
2662+ ///
2663+ /// __Default value:__ `0.3`
2664+ let minOpacity: Double?
2665+ /// Default minimum value for point size scale with zero=false.
2666+ ///
2667+ /// __Default value:__ `9`
2668+ let minSize: Double?
2669+ /// Default minimum strokeWidth for strokeWidth (or rule/line's size) scale with zero=false.
2670+ ///
2671+ /// __Default value:__ `1`
2672+ let minStrokeWidth: Double?
2673+ /// Default outer padding for `x` and `y` point-ordinal scales.
2674+ ///
2675+ /// __Default value:__ `0.5`
2676+ let pointPadding: Double?
2677+ /// Default range step for band and point scales of (1) the `y` channel
2678+ /// and (2) the `x` channel when the mark is not `text`.
2679+ ///
2680+ /// __Default value:__ `21`
2681+ let rangeStep: Double?
2682+ /// If true, rounds numeric output values to integers.
2683+ /// This can be helpful for snapping to the pixel grid.
2684+ /// (Only available for `x`, `y`, and `size` scales.)
2685+ let round: Bool?
2686+ /// Default range step for `x` band and point scales of text marks.
2687+ ///
2688+ /// __Default value:__ `90`
2689+ let textXRangeStep: Double?
2690+ /// Use the source data range before aggregation as scale domain instead of aggregated data
2691+ /// for aggregate axis.
2692+ ///
2693+ /// This is equivalent to setting `domain` to `"unaggregate"` for aggregated _quantitative_
2694+ /// fields by default.
2695+ ///
2696+ /// This property only works with aggregate functions that produce values within the raw data
2697+ /// domain (`"mean"`, `"average"`, `"median"`, `"q1"`, `"q3"`, `"min"`, `"max"`). For other
2698+ /// aggregations that produce values outside of the raw data domain (e.g. `"count"`,
2699+ /// `"sum"`), this property is ignored.
2700+ ///
2701+ /// __Default value:__ `false`
2702+ let useUnaggregatedDomain: Bool?
2703+
2704+ enum CodingKeys: String, CodingKey {
2705+ case bandPaddingInner = "bandPaddingInner"
2706+ case bandPaddingOuter = "bandPaddingOuter"
2707+ case clamp = "clamp"
2708+ case continuousPadding = "continuousPadding"
2709+ case maxBandSize = "maxBandSize"
2710+ case maxFontSize = "maxFontSize"
2711+ case maxOpacity = "maxOpacity"
2712+ case maxSize = "maxSize"
2713+ case maxStrokeWidth = "maxStrokeWidth"
2714+ case minBandSize = "minBandSize"
2715+ case minFontSize = "minFontSize"
2716+ case minOpacity = "minOpacity"
2717+ case minSize = "minSize"
2718+ case minStrokeWidth = "minStrokeWidth"
2719+ case pointPadding = "pointPadding"
2720+ case rangeStep = "rangeStep"
2721+ case round = "round"
2722+ case textXRangeStep = "textXRangeStep"
2723+ case useUnaggregatedDomain = "useUnaggregatedDomain"
2724+ }
2725+}
2726+
2727+// MARK: ScaleConfig convenience initializers and mutators
2728+
2729+extension ScaleConfig {
2730+ init(data: Data) throws {
2731+ self = try newJSONDecoder().decode(ScaleConfig.self, from: data)
2732+ }
2733+
2734+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2735+ guard let data = json.data(using: encoding) else {
2736+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2737+ }
2738+ try self.init(data: data)
2739+ }
2740+
2741+ init(fromURL url: URL) throws {
2742+ try self.init(data: try Data(contentsOf: url))
2743+ }
2744+
2745+ func with(
2746+ bandPaddingInner: Double?? = nil,
2747+ bandPaddingOuter: Double?? = nil,
2748+ clamp: Bool?? = nil,
2749+ continuousPadding: Double?? = nil,
2750+ maxBandSize: Double?? = nil,
2751+ maxFontSize: Double?? = nil,
2752+ maxOpacity: Double?? = nil,
2753+ maxSize: Double?? = nil,
2754+ maxStrokeWidth: Double?? = nil,
2755+ minBandSize: Double?? = nil,
2756+ minFontSize: Double?? = nil,
2757+ minOpacity: Double?? = nil,
2758+ minSize: Double?? = nil,
2759+ minStrokeWidth: Double?? = nil,
2760+ pointPadding: Double?? = nil,
2761+ rangeStep: Double?? = nil,
2762+ round: Bool?? = nil,
2763+ textXRangeStep: Double?? = nil,
2764+ useUnaggregatedDomain: Bool?? = nil
2765+ ) -> ScaleConfig {
2766+ return ScaleConfig(
2767+ bandPaddingInner: bandPaddingInner ?? self.bandPaddingInner,
2768+ bandPaddingOuter: bandPaddingOuter ?? self.bandPaddingOuter,
2769+ clamp: clamp ?? self.clamp,
2770+ continuousPadding: continuousPadding ?? self.continuousPadding,
2771+ maxBandSize: maxBandSize ?? self.maxBandSize,
2772+ maxFontSize: maxFontSize ?? self.maxFontSize,
2773+ maxOpacity: maxOpacity ?? self.maxOpacity,
2774+ maxSize: maxSize ?? self.maxSize,
2775+ maxStrokeWidth: maxStrokeWidth ?? self.maxStrokeWidth,
2776+ minBandSize: minBandSize ?? self.minBandSize,
2777+ minFontSize: minFontSize ?? self.minFontSize,
2778+ minOpacity: minOpacity ?? self.minOpacity,
2779+ minSize: minSize ?? self.minSize,
2780+ minStrokeWidth: minStrokeWidth ?? self.minStrokeWidth,
2781+ pointPadding: pointPadding ?? self.pointPadding,
2782+ rangeStep: rangeStep ?? self.rangeStep,
2783+ round: round ?? self.round,
2784+ textXRangeStep: textXRangeStep ?? self.textXRangeStep,
2785+ useUnaggregatedDomain: useUnaggregatedDomain ?? self.useUnaggregatedDomain
2786+ )
2787+ }
2788+
2789+ func jsonData() throws -> Data {
2790+ return try newJSONEncoder().encode(self)
2791+ }
2792+
2793+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2794+ return String(data: try self.jsonData(), encoding: encoding)
2795+ }
2796+}
2797+
2798+/// An object hash for defining default properties for each type of selections.
2799+// MARK: - SelectionConfig
2800+struct SelectionConfig: Codable {
2801+ /// The default definition for an [`interval`](selection.html#type) selection. All properties
2802+ /// and transformations
2803+ /// for an interval selection definition (except `type`) may be specified here.
2804+ ///
2805+ /// For instance, setting `interval` to `{"translate": false}` disables the ability to move
2806+ /// interval selections by default.
2807+ let interval: IntervalSelectionConfig?
2808+ /// The default definition for a [`multi`](selection.html#type) selection. All properties and
2809+ /// transformations
2810+ /// for a multi selection definition (except `type`) may be specified here.
2811+ ///
2812+ /// For instance, setting `multi` to `{"toggle": "event.altKey"}` adds additional values to
2813+ /// multi selections when clicking with the alt-key pressed by default.
2814+ let multi: MultiSelectionConfig?
2815+ /// The default definition for a [`single`](selection.html#type) selection. All properties
2816+ /// and transformations
2817+ /// for a single selection definition (except `type`) may be specified here.
2818+ ///
2819+ /// For instance, setting `single` to `{"on": "dblclick"}` populates single selections on
2820+ /// double-click by default.
2821+ let single: SingleSelectionConfig?
2822+
2823+ enum CodingKeys: String, CodingKey {
2824+ case interval = "interval"
2825+ case multi = "multi"
2826+ case single = "single"
2827+ }
2828+}
2829+
2830+// MARK: SelectionConfig convenience initializers and mutators
2831+
2832+extension SelectionConfig {
2833+ init(data: Data) throws {
2834+ self = try newJSONDecoder().decode(SelectionConfig.self, from: data)
2835+ }
2836+
2837+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2838+ guard let data = json.data(using: encoding) else {
2839+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2840+ }
2841+ try self.init(data: data)
2842+ }
2843+
2844+ init(fromURL url: URL) throws {
2845+ try self.init(data: try Data(contentsOf: url))
2846+ }
2847+
2848+ func with(
2849+ interval: IntervalSelectionConfig?? = nil,
2850+ multi: MultiSelectionConfig?? = nil,
2851+ single: SingleSelectionConfig?? = nil
2852+ ) -> SelectionConfig {
2853+ return SelectionConfig(
2854+ interval: interval ?? self.interval,
2855+ multi: multi ?? self.multi,
2856+ single: single ?? self.single
2857+ )
2858+ }
2859+
2860+ func jsonData() throws -> Data {
2861+ return try newJSONEncoder().encode(self)
2862+ }
2863+
2864+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2865+ return String(data: try self.jsonData(), encoding: encoding)
2866+ }
2867+}
2868+
2869+/// The default definition for an [`interval`](selection.html#type) selection. All properties
2870+/// and transformations
2871+/// for an interval selection definition (except `type`) may be specified here.
2872+///
2873+/// For instance, setting `interval` to `{"translate": false}` disables the ability to move
2874+/// interval selections by default.
2875+// MARK: - IntervalSelectionConfig
2876+struct IntervalSelectionConfig: Codable {
2877+ /// Establishes a two-way binding between the interval selection and the scales
2878+ /// used within the same view. This allows a user to interactively pan and
2879+ /// zoom the view.
2880+ let bind: BindEnum?
2881+ /// By default, all data values are considered to lie within an empty selection.
2882+ /// When set to `none`, empty selections contain no data values.
2883+ let empty: Empty?
2884+ /// An array of encoding channels. The corresponding data field values
2885+ /// must match for a data tuple to fall within the selection.
2886+ let encodings: [SingleDefChannel]?
2887+ /// An array of field names whose values must match for a data tuple to
2888+ /// fall within the selection.
2889+ let fields: [String]?
2890+ /// An interval selection also adds a rectangle mark to depict the
2891+ /// extents of the interval. The `mark` property can be used to customize the
2892+ /// appearance of the mark.
2893+ let mark: BrushConfig?
2894+ /// A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
2895+ /// selector) that triggers the selection.
2896+ /// For interval selections, the event stream must specify a [start and
2897+ /// end](https://vega.github.io/vega/docs/event-streams/#between-filters).
2898+ let on: JSONAny?
2899+ /// With layered and multi-view displays, a strategy that determines how
2900+ /// selections' data queries are resolved when applied in a filter transform,
2901+ /// conditional encoding rule, or scale domain.
2902+ let resolve: SelectionResolution?
2903+ /// When truthy, allows a user to interactively move an interval selection
2904+ /// back-and-forth. Can be `true`, `false` (to disable panning), or a
2905+ /// [Vega event stream definition](https://vega.github.io/vega/docs/event-streams/)
2906+ /// which must include a start and end event to trigger continuous panning.
2907+ ///
2908+ /// __Default value:__ `true`, which corresponds to
2909+ /// `[mousedown, window:mouseup] > window:mousemove!` which corresponds to
2910+ /// clicks and dragging within an interval selection to reposition it.
2911+ let translate: Translate?
2912+ /// When truthy, allows a user to interactively resize an interval selection.
2913+ /// Can be `true`, `false` (to disable zooming), or a [Vega event stream
2914+ /// definition](https://vega.github.io/vega/docs/event-streams/). Currently,
2915+ /// only `wheel` events are supported.
2916+ ///
2917+ ///
2918+ /// __Default value:__ `true`, which corresponds to `wheel!`.
2919+ let zoom: Translate?
2920+
2921+ enum CodingKeys: String, CodingKey {
2922+ case bind = "bind"
2923+ case empty = "empty"
2924+ case encodings = "encodings"
2925+ case fields = "fields"
2926+ case mark = "mark"
2927+ case on = "on"
2928+ case resolve = "resolve"
2929+ case translate = "translate"
2930+ case zoom = "zoom"
2931+ }
2932+}
2933+
2934+// MARK: IntervalSelectionConfig convenience initializers and mutators
2935+
2936+extension IntervalSelectionConfig {
2937+ init(data: Data) throws {
2938+ self = try newJSONDecoder().decode(IntervalSelectionConfig.self, from: data)
2939+ }
2940+
2941+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
2942+ guard let data = json.data(using: encoding) else {
2943+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
2944+ }
2945+ try self.init(data: data)
2946+ }
2947+
2948+ init(fromURL url: URL) throws {
2949+ try self.init(data: try Data(contentsOf: url))
2950+ }
2951+
2952+ func with(
2953+ bind: BindEnum?? = nil,
2954+ empty: Empty?? = nil,
2955+ encodings: [SingleDefChannel]?? = nil,
2956+ fields: [String]?? = nil,
2957+ mark: BrushConfig?? = nil,
2958+ on: JSONAny?? = nil,
2959+ resolve: SelectionResolution?? = nil,
2960+ translate: Translate?? = nil,
2961+ zoom: Translate?? = nil
2962+ ) -> IntervalSelectionConfig {
2963+ return IntervalSelectionConfig(
2964+ bind: bind ?? self.bind,
2965+ empty: empty ?? self.empty,
2966+ encodings: encodings ?? self.encodings,
2967+ fields: fields ?? self.fields,
2968+ mark: mark ?? self.mark,
2969+ on: on ?? self.on,
2970+ resolve: resolve ?? self.resolve,
2971+ translate: translate ?? self.translate,
2972+ zoom: zoom ?? self.zoom
2973+ )
2974+ }
2975+
2976+ func jsonData() throws -> Data {
2977+ return try newJSONEncoder().encode(self)
2978+ }
2979+
2980+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
2981+ return String(data: try self.jsonData(), encoding: encoding)
2982+ }
2983+}
2984+
2985+/// Establishes a two-way binding between the interval selection and the scales
2986+/// used within the same view. This allows a user to interactively pan and
2987+/// zoom the view.
2988+enum BindEnum: String, Codable {
2989+ case scales = "scales"
2990+}
2991+
2992+/// By default, all data values are considered to lie within an empty selection.
2993+/// When set to `none`, empty selections contain no data values.
2994+enum Empty: String, Codable {
2995+ case all = "all"
2996+ case none = "none"
2997+}
2998+
2999+enum SingleDefChannel: String, Codable {
3000+ case x = "x"
3001+ case y = "y"
3002+ case x2 = "x2"
3003+ case y2 = "y2"
3004+ case row = "row"
3005+ case column = "column"
3006+ case size = "size"
3007+ case shape = "shape"
3008+ case color = "color"
3009+ case opacity = "opacity"
3010+ case text = "text"
3011+ case tooltip = "tooltip"
3012+ case href = "href"
3013+}
3014+
3015+/// An interval selection also adds a rectangle mark to depict the
3016+/// extents of the interval. The `mark` property can be used to customize the
3017+/// appearance of the mark.
3018+// MARK: - BrushConfig
3019+struct BrushConfig: Codable {
3020+ /// The fill color of the interval mark.
3021+ ///
3022+ /// __Default value:__ `#333333`
3023+ let fill: String?
3024+ /// The fill opacity of the interval mark (a value between 0 and 1).
3025+ ///
3026+ /// __Default value:__ `0.125`
3027+ let fillOpacity: Double?
3028+ /// The stroke color of the interval mark.
3029+ ///
3030+ /// __Default value:__ `#ffffff`
3031+ let stroke: String?
3032+ /// An array of alternating stroke and space lengths,
3033+ /// for creating dashed or dotted lines.
3034+ let strokeDash: [Double]?
3035+ /// The offset (in pixels) with which to begin drawing the stroke dash array.
3036+ let strokeDashOffset: Double?
3037+ /// The stroke opacity of the interval mark (a value between 0 and 1).
3038+ let strokeOpacity: Double?
3039+ /// The stroke width of the interval mark.
3040+ let strokeWidth: Double?
3041+
3042+ enum CodingKeys: String, CodingKey {
3043+ case fill = "fill"
3044+ case fillOpacity = "fillOpacity"
3045+ case stroke = "stroke"
3046+ case strokeDash = "strokeDash"
3047+ case strokeDashOffset = "strokeDashOffset"
3048+ case strokeOpacity = "strokeOpacity"
3049+ case strokeWidth = "strokeWidth"
3050+ }
3051+}
3052+
3053+// MARK: BrushConfig convenience initializers and mutators
3054+
3055+extension BrushConfig {
3056+ init(data: Data) throws {
3057+ self = try newJSONDecoder().decode(BrushConfig.self, from: data)
3058+ }
3059+
3060+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
3061+ guard let data = json.data(using: encoding) else {
3062+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
3063+ }
3064+ try self.init(data: data)
3065+ }
3066+
3067+ init(fromURL url: URL) throws {
3068+ try self.init(data: try Data(contentsOf: url))
3069+ }
3070+
3071+ func with(
3072+ fill: String?? = nil,
3073+ fillOpacity: Double?? = nil,
3074+ stroke: String?? = nil,
3075+ strokeDash: [Double]?? = nil,
3076+ strokeDashOffset: Double?? = nil,
3077+ strokeOpacity: Double?? = nil,
3078+ strokeWidth: Double?? = nil
3079+ ) -> BrushConfig {
3080+ return BrushConfig(
3081+ fill: fill ?? self.fill,
3082+ fillOpacity: fillOpacity ?? self.fillOpacity,
3083+ stroke: stroke ?? self.stroke,
3084+ strokeDash: strokeDash ?? self.strokeDash,
3085+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
3086+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
3087+ strokeWidth: strokeWidth ?? self.strokeWidth
3088+ )
3089+ }
3090+
3091+ func jsonData() throws -> Data {
3092+ return try newJSONEncoder().encode(self)
3093+ }
3094+
3095+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
3096+ return String(data: try self.jsonData(), encoding: encoding)
3097+ }
3098+}
3099+
3100+/// With layered and multi-view displays, a strategy that determines how
3101+/// selections' data queries are resolved when applied in a filter transform,
3102+/// conditional encoding rule, or scale domain.
3103+enum SelectionResolution: String, Codable {
3104+ case global = "global"
3105+ case union = "union"
3106+ case intersect = "intersect"
3107+}
3108+
3109+/// When truthy, allows a user to interactively move an interval selection
3110+/// back-and-forth. Can be `true`, `false` (to disable panning), or a
3111+/// [Vega event stream definition](https://vega.github.io/vega/docs/event-streams/)
3112+/// which must include a start and end event to trigger continuous panning.
3113+///
3114+/// __Default value:__ `true`, which corresponds to
3115+/// `[mousedown, window:mouseup] > window:mousemove!` which corresponds to
3116+/// clicks and dragging within an interval selection to reposition it.
3117+///
3118+/// When truthy, allows a user to interactively resize an interval selection.
3119+/// Can be `true`, `false` (to disable zooming), or a [Vega event stream
3120+/// definition](https://vega.github.io/vega/docs/event-streams/). Currently,
3121+/// only `wheel` events are supported.
3122+///
3123+///
3124+/// __Default value:__ `true`, which corresponds to `wheel!`.
3125+///
3126+/// Controls whether data values should be toggled or only ever inserted into
3127+/// multi selections. Can be `true`, `false` (for insertion only), or a
3128+/// [Vega expression](https://vega.github.io/vega/docs/expressions/).
3129+///
3130+/// __Default value:__ `true`, which corresponds to `event.shiftKey` (i.e.,
3131+/// data values are toggled when a user interacts with the shift-key pressed).
3132+///
3133+/// See the [toggle transform](toggle.html) documentation for more information.
3134+enum Translate: Codable {
3135+ case bool(Bool)
3136+ case string(String)
3137+
3138+ init(from decoder: Decoder) throws {
3139+ let container = try decoder.singleValueContainer()
3140+ if let x = try? container.decode(Bool.self) {
3141+ self = .bool(x)
3142+ return
3143+ }
3144+ if let x = try? container.decode(String.self) {
3145+ self = .string(x)
3146+ return
3147+ }
3148+ throw DecodingError.typeMismatch(Translate.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Translate"))
3149+ }
3150+
3151+ func encode(to encoder: Encoder) throws {
3152+ var container = encoder.singleValueContainer()
3153+ switch self {
3154+ case .bool(let x):
3155+ try container.encode(x)
3156+ case .string(let x):
3157+ try container.encode(x)
3158+ }
3159+ }
3160+}
3161+
3162+/// The default definition for a [`multi`](selection.html#type) selection. All properties and
3163+/// transformations
3164+/// for a multi selection definition (except `type`) may be specified here.
3165+///
3166+/// For instance, setting `multi` to `{"toggle": "event.altKey"}` adds additional values to
3167+/// multi selections when clicking with the alt-key pressed by default.
3168+// MARK: - MultiSelectionConfig
3169+struct MultiSelectionConfig: Codable {
3170+ /// By default, all data values are considered to lie within an empty selection.
3171+ /// When set to `none`, empty selections contain no data values.
3172+ let empty: Empty?
3173+ /// An array of encoding channels. The corresponding data field values
3174+ /// must match for a data tuple to fall within the selection.
3175+ let encodings: [SingleDefChannel]?
3176+ /// An array of field names whose values must match for a data tuple to
3177+ /// fall within the selection.
3178+ let fields: [String]?
3179+ /// When true, an invisible voronoi diagram is computed to accelerate discrete
3180+ /// selection. The data value _nearest_ the mouse cursor is added to the selection.
3181+ ///
3182+ /// See the [nearest transform](nearest.html) documentation for more information.
3183+ let nearest: Bool?
3184+ /// A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
3185+ /// selector) that triggers the selection.
3186+ /// For interval selections, the event stream must specify a [start and
3187+ /// end](https://vega.github.io/vega/docs/event-streams/#between-filters).
3188+ let on: JSONAny?
3189+ /// With layered and multi-view displays, a strategy that determines how
3190+ /// selections' data queries are resolved when applied in a filter transform,
3191+ /// conditional encoding rule, or scale domain.
3192+ let resolve: SelectionResolution?
3193+ /// Controls whether data values should be toggled or only ever inserted into
3194+ /// multi selections. Can be `true`, `false` (for insertion only), or a
3195+ /// [Vega expression](https://vega.github.io/vega/docs/expressions/).
3196+ ///
3197+ /// __Default value:__ `true`, which corresponds to `event.shiftKey` (i.e.,
3198+ /// data values are toggled when a user interacts with the shift-key pressed).
3199+ ///
3200+ /// See the [toggle transform](toggle.html) documentation for more information.
3201+ let toggle: Translate?
3202+
3203+ enum CodingKeys: String, CodingKey {
3204+ case empty = "empty"
3205+ case encodings = "encodings"
3206+ case fields = "fields"
3207+ case nearest = "nearest"
3208+ case on = "on"
3209+ case resolve = "resolve"
3210+ case toggle = "toggle"
3211+ }
3212+}
3213+
3214+// MARK: MultiSelectionConfig convenience initializers and mutators
3215+
3216+extension MultiSelectionConfig {
3217+ init(data: Data) throws {
3218+ self = try newJSONDecoder().decode(MultiSelectionConfig.self, from: data)
3219+ }
3220+
3221+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
3222+ guard let data = json.data(using: encoding) else {
3223+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
3224+ }
3225+ try self.init(data: data)
3226+ }
3227+
3228+ init(fromURL url: URL) throws {
3229+ try self.init(data: try Data(contentsOf: url))
3230+ }
3231+
3232+ func with(
3233+ empty: Empty?? = nil,
3234+ encodings: [SingleDefChannel]?? = nil,
3235+ fields: [String]?? = nil,
3236+ nearest: Bool?? = nil,
3237+ on: JSONAny?? = nil,
3238+ resolve: SelectionResolution?? = nil,
3239+ toggle: Translate?? = nil
3240+ ) -> MultiSelectionConfig {
3241+ return MultiSelectionConfig(
3242+ empty: empty ?? self.empty,
3243+ encodings: encodings ?? self.encodings,
3244+ fields: fields ?? self.fields,
3245+ nearest: nearest ?? self.nearest,
3246+ on: on ?? self.on,
3247+ resolve: resolve ?? self.resolve,
3248+ toggle: toggle ?? self.toggle
3249+ )
3250+ }
3251+
3252+ func jsonData() throws -> Data {
3253+ return try newJSONEncoder().encode(self)
3254+ }
3255+
3256+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
3257+ return String(data: try self.jsonData(), encoding: encoding)
3258+ }
3259+}
3260+
3261+/// The default definition for a [`single`](selection.html#type) selection. All properties
3262+/// and transformations
3263+/// for a single selection definition (except `type`) may be specified here.
3264+///
3265+/// For instance, setting `single` to `{"on": "dblclick"}` populates single selections on
3266+/// double-click by default.
3267+// MARK: - SingleSelectionConfig
3268+struct SingleSelectionConfig: Codable {
3269+ /// Establish a two-way binding between a single selection and input elements
3270+ /// (also known as dynamic query widgets). A binding takes the form of
3271+ /// Vega's [input element binding definition](https://vega.github.io/vega/docs/signals/#bind)
3272+ /// or can be a mapping between projected field/encodings and binding definitions.
3273+ ///
3274+ /// See the [bind transform](bind.html) documentation for more information.
3275+ let bind: [String: VGBinding]?
3276+ /// By default, all data values are considered to lie within an empty selection.
3277+ /// When set to `none`, empty selections contain no data values.
3278+ let empty: Empty?
3279+ /// An array of encoding channels. The corresponding data field values
3280+ /// must match for a data tuple to fall within the selection.
3281+ let encodings: [SingleDefChannel]?
3282+ /// An array of field names whose values must match for a data tuple to
3283+ /// fall within the selection.
3284+ let fields: [String]?
3285+ /// When true, an invisible voronoi diagram is computed to accelerate discrete
3286+ /// selection. The data value _nearest_ the mouse cursor is added to the selection.
3287+ ///
3288+ /// See the [nearest transform](nearest.html) documentation for more information.
3289+ let nearest: Bool?
3290+ /// A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
3291+ /// selector) that triggers the selection.
3292+ /// For interval selections, the event stream must specify a [start and
3293+ /// end](https://vega.github.io/vega/docs/event-streams/#between-filters).
3294+ let on: JSONAny?
3295+ /// With layered and multi-view displays, a strategy that determines how
3296+ /// selections' data queries are resolved when applied in a filter transform,
3297+ /// conditional encoding rule, or scale domain.
3298+ let resolve: SelectionResolution?
3299+
3300+ enum CodingKeys: String, CodingKey {
3301+ case bind = "bind"
3302+ case empty = "empty"
3303+ case encodings = "encodings"
3304+ case fields = "fields"
3305+ case nearest = "nearest"
3306+ case on = "on"
3307+ case resolve = "resolve"
3308+ }
3309+}
3310+
3311+// MARK: SingleSelectionConfig convenience initializers and mutators
3312+
3313+extension SingleSelectionConfig {
3314+ init(data: Data) throws {
3315+ self = try newJSONDecoder().decode(SingleSelectionConfig.self, from: data)
3316+ }
3317+
3318+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
3319+ guard let data = json.data(using: encoding) else {
3320+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
3321+ }
3322+ try self.init(data: data)
3323+ }
3324+
3325+ init(fromURL url: URL) throws {
3326+ try self.init(data: try Data(contentsOf: url))
3327+ }
3328+
3329+ func with(
3330+ bind: [String: VGBinding]?? = nil,
3331+ empty: Empty?? = nil,
3332+ encodings: [SingleDefChannel]?? = nil,
3333+ fields: [String]?? = nil,
3334+ nearest: Bool?? = nil,
3335+ on: JSONAny?? = nil,
3336+ resolve: SelectionResolution?? = nil
3337+ ) -> SingleSelectionConfig {
3338+ return SingleSelectionConfig(
3339+ bind: bind ?? self.bind,
3340+ empty: empty ?? self.empty,
3341+ encodings: encodings ?? self.encodings,
3342+ fields: fields ?? self.fields,
3343+ nearest: nearest ?? self.nearest,
3344+ on: on ?? self.on,
3345+ resolve: resolve ?? self.resolve
3346+ )
3347+ }
3348+
3349+ func jsonData() throws -> Data {
3350+ return try newJSONEncoder().encode(self)
3351+ }
3352+
3353+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
3354+ return String(data: try self.jsonData(), encoding: encoding)
3355+ }
3356+}
3357+
3358+// MARK: - VGBinding
3359+struct VGBinding: Codable {
3360+ let element: String?
3361+ let input: String
3362+ let options: [String]?
3363+ let max: Double?
3364+ let min: Double?
3365+ let step: Double?
3366+
3367+ enum CodingKeys: String, CodingKey {
3368+ case element = "element"
3369+ case input = "input"
3370+ case options = "options"
3371+ case max = "max"
3372+ case min = "min"
3373+ case step = "step"
3374+ }
3375+}
3376+
3377+// MARK: VGBinding convenience initializers and mutators
3378+
3379+extension VGBinding {
3380+ init(data: Data) throws {
3381+ self = try newJSONDecoder().decode(VGBinding.self, from: data)
3382+ }
3383+
3384+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
3385+ guard let data = json.data(using: encoding) else {
3386+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
3387+ }
3388+ try self.init(data: data)
3389+ }
3390+
3391+ init(fromURL url: URL) throws {
3392+ try self.init(data: try Data(contentsOf: url))
3393+ }
3394+
3395+ func with(
3396+ element: String?? = nil,
3397+ input: String? = nil,
3398+ options: [String]?? = nil,
3399+ max: Double?? = nil,
3400+ min: Double?? = nil,
3401+ step: Double?? = nil
3402+ ) -> VGBinding {
3403+ return VGBinding(
3404+ element: element ?? self.element,
3405+ input: input ?? self.input,
3406+ options: options ?? self.options,
3407+ max: max ?? self.max,
3408+ min: min ?? self.min,
3409+ step: step ?? self.step
3410+ )
3411+ }
3412+
3413+ func jsonData() throws -> Data {
3414+ return try newJSONEncoder().encode(self)
3415+ }
3416+
3417+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
3418+ return String(data: try self.jsonData(), encoding: encoding)
3419+ }
3420+}
3421+
3422+/// Default stack offset for stackable mark.
3423+enum StackOffset: String, Codable {
3424+ case zero = "zero"
3425+ case center = "center"
3426+ case normalize = "normalize"
3427+}
3428+
3429+// MARK: - VGMarkConfig
3430+struct VGMarkConfig: Codable {
3431+ /// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
3432+ let align: HorizontalAlign?
3433+ /// The rotation angle of the text, in degrees.
3434+ let angle: Double?
3435+ /// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
3436+ ///
3437+ /// __Default value:__ `"middle"`
3438+ let baseline: VerticalAlign?
3439+ /// The mouse cursor used over the mark. Any valid [CSS cursor
3440+ /// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
3441+ let cursor: Cursor?
3442+ /// The horizontal offset, in pixels, between the text label and its anchor point. The offset
3443+ /// is applied after rotation by the _angle_ property.
3444+ let dx: Double?
3445+ /// The vertical offset, in pixels, between the text label and its anchor point. The offset
3446+ /// is applied after rotation by the _angle_ property.
3447+ let dy: Double?
3448+ /// Default Fill Color. This has higher precedence than config.color
3449+ ///
3450+ /// __Default value:__ (None)
3451+ let fill: String?
3452+ /// The fill opacity (value between [0,1]).
3453+ ///
3454+ /// __Default value:__ `1`
3455+ let fillOpacity: Double?
3456+ /// The typeface to set the text in (e.g., `"Helvetica Neue"`).
3457+ let font: String?
3458+ /// The font size, in pixels.
3459+ let fontSize: Double?
3460+ /// The font style (e.g., `"italic"`).
3461+ let fontStyle: FontStyle?
3462+ /// The font weight (e.g., `"bold"`).
3463+ let fontWeight: FontWeightUnion?
3464+ /// A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
3465+ let href: String?
3466+ /// The line interpolation method to use for line and area marks. One of the following:
3467+ /// - `"linear"`: piecewise linear segments, as in a polyline.
3468+ /// - `"linear-closed"`: close the linear segments to form a polygon.
3469+ /// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
3470+ /// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
3471+ /// function.
3472+ /// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
3473+ /// function.
3474+ /// - `"basis"`: a B-spline, with control point duplication on the ends.
3475+ /// - `"basis-open"`: an open B-spline; may not intersect the start or end.
3476+ /// - `"basis-closed"`: a closed B-spline, as in a loop.
3477+ /// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
3478+ /// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
3479+ /// will intersect other control points.
3480+ /// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
3481+ /// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
3482+ /// spline.
3483+ /// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
3484+ let interpolate: Interpolate?
3485+ /// The maximum length of the text mark in pixels (default 0, indicating no limit). The text
3486+ /// value will be automatically truncated if the rendered size exceeds the limit.
3487+ let limit: Double?
3488+ /// The overall opacity (value between [0,1]).
3489+ ///
3490+ /// __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
3491+ /// `square` marks or layered `bar` charts and `1` otherwise.
3492+ let opacity: Double?
3493+ /// The orientation of a non-stacked bar, tick, area, and line charts.
3494+ /// The value is either horizontal (default) or vertical.
3495+ /// - For bar, rule and tick, this determines whether the size of the bar and tick
3496+ /// should be applied to x or y dimension.
3497+ /// - For area, this property determines the orient property of the Vega output.
3498+ /// - For line, this property determines the sort order of the points in the line
3499+ /// if `config.sortLineBy` is not specified.
3500+ /// For stacked charts, this is always determined by the orientation of the stack;
3501+ /// therefore explicitly specified value will be ignored.
3502+ let orient: Orient?
3503+ /// Polar coordinate radial offset, in pixels, of the text label from the origin determined
3504+ /// by the `x` and `y` properties.
3505+ let radius: Double?
3506+ /// The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
3507+ /// `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
3508+ ///
3509+ /// __Default value:__ `"circle"`
3510+ let shape: String?
3511+ /// The pixel area each the point/circle/square.
3512+ /// For example: in the case of circles, the radius is determined in part by the square root
3513+ /// of the size value.
3514+ ///
3515+ /// __Default value:__ `30`
3516+ let size: Double?
3517+ /// Default Stroke Color. This has higher precedence than config.color
3518+ ///
3519+ /// __Default value:__ (None)
3520+ let stroke: String?
3521+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
3522+ let strokeDash: [Double]?
3523+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
3524+ let strokeDashOffset: Double?
3525+ /// The stroke opacity (value between [0,1]).
3526+ ///
3527+ /// __Default value:__ `1`
3528+ let strokeOpacity: Double?
3529+ /// The stroke width, in pixels.
3530+ let strokeWidth: Double?
3531+ /// Depending on the interpolation type, sets the tension parameter (for line and area marks).
3532+ let tension: Double?
3533+ /// Placeholder text if the `text` channel is not specified
3534+ let text: String?
3535+ /// Polar coordinate angle, in radians, of the text label from the origin determined by the
3536+ /// `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
3537+ /// `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
3538+ /// indicating "north".
3539+ let theta: Double?
3540+
3541+ enum CodingKeys: String, CodingKey {
3542+ case align = "align"
3543+ case angle = "angle"
3544+ case baseline = "baseline"
3545+ case cursor = "cursor"
3546+ case dx = "dx"
3547+ case dy = "dy"
3548+ case fill = "fill"
3549+ case fillOpacity = "fillOpacity"
3550+ case font = "font"
3551+ case fontSize = "fontSize"
3552+ case fontStyle = "fontStyle"
3553+ case fontWeight = "fontWeight"
3554+ case href = "href"
3555+ case interpolate = "interpolate"
3556+ case limit = "limit"
3557+ case opacity = "opacity"
3558+ case orient = "orient"
3559+ case radius = "radius"
3560+ case shape = "shape"
3561+ case size = "size"
3562+ case stroke = "stroke"
3563+ case strokeDash = "strokeDash"
3564+ case strokeDashOffset = "strokeDashOffset"
3565+ case strokeOpacity = "strokeOpacity"
3566+ case strokeWidth = "strokeWidth"
3567+ case tension = "tension"
3568+ case text = "text"
3569+ case theta = "theta"
3570+ }
3571+}
3572+
3573+// MARK: VGMarkConfig convenience initializers and mutators
3574+
3575+extension VGMarkConfig {
3576+ init(data: Data) throws {
3577+ self = try newJSONDecoder().decode(VGMarkConfig.self, from: data)
3578+ }
3579+
3580+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
3581+ guard let data = json.data(using: encoding) else {
3582+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
3583+ }
3584+ try self.init(data: data)
3585+ }
3586+
3587+ init(fromURL url: URL) throws {
3588+ try self.init(data: try Data(contentsOf: url))
3589+ }
3590+
3591+ func with(
3592+ align: HorizontalAlign?? = nil,
3593+ angle: Double?? = nil,
3594+ baseline: VerticalAlign?? = nil,
3595+ cursor: Cursor?? = nil,
3596+ dx: Double?? = nil,
3597+ dy: Double?? = nil,
3598+ fill: String?? = nil,
3599+ fillOpacity: Double?? = nil,
3600+ font: String?? = nil,
3601+ fontSize: Double?? = nil,
3602+ fontStyle: FontStyle?? = nil,
3603+ fontWeight: FontWeightUnion?? = nil,
3604+ href: String?? = nil,
3605+ interpolate: Interpolate?? = nil,
3606+ limit: Double?? = nil,
3607+ opacity: Double?? = nil,
3608+ orient: Orient?? = nil,
3609+ radius: Double?? = nil,
3610+ shape: String?? = nil,
3611+ size: Double?? = nil,
3612+ stroke: String?? = nil,
3613+ strokeDash: [Double]?? = nil,
3614+ strokeDashOffset: Double?? = nil,
3615+ strokeOpacity: Double?? = nil,
3616+ strokeWidth: Double?? = nil,
3617+ tension: Double?? = nil,
3618+ text: String?? = nil,
3619+ theta: Double?? = nil
3620+ ) -> VGMarkConfig {
3621+ return VGMarkConfig(
3622+ align: align ?? self.align,
3623+ angle: angle ?? self.angle,
3624+ baseline: baseline ?? self.baseline,
3625+ cursor: cursor ?? self.cursor,
3626+ dx: dx ?? self.dx,
3627+ dy: dy ?? self.dy,
3628+ fill: fill ?? self.fill,
3629+ fillOpacity: fillOpacity ?? self.fillOpacity,
3630+ font: font ?? self.font,
3631+ fontSize: fontSize ?? self.fontSize,
3632+ fontStyle: fontStyle ?? self.fontStyle,
3633+ fontWeight: fontWeight ?? self.fontWeight,
3634+ href: href ?? self.href,
3635+ interpolate: interpolate ?? self.interpolate,
3636+ limit: limit ?? self.limit,
3637+ opacity: opacity ?? self.opacity,
3638+ orient: orient ?? self.orient,
3639+ radius: radius ?? self.radius,
3640+ shape: shape ?? self.shape,
3641+ size: size ?? self.size,
3642+ stroke: stroke ?? self.stroke,
3643+ strokeDash: strokeDash ?? self.strokeDash,
3644+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
3645+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
3646+ strokeWidth: strokeWidth ?? self.strokeWidth,
3647+ tension: tension ?? self.tension,
3648+ text: text ?? self.text,
3649+ theta: theta ?? self.theta
3650+ )
3651+ }
3652+
3653+ func jsonData() throws -> Data {
3654+ return try newJSONEncoder().encode(self)
3655+ }
3656+
3657+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
3658+ return String(data: try self.jsonData(), encoding: encoding)
3659+ }
3660+}
3661+
3662+/// Text-Specific Config
3663+// MARK: - TextConfig
3664+struct TextConfig: Codable {
3665+ /// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
3666+ let align: HorizontalAlign?
3667+ /// The rotation angle of the text, in degrees.
3668+ let angle: Double?
3669+ /// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
3670+ ///
3671+ /// __Default value:__ `"middle"`
3672+ let baseline: VerticalAlign?
3673+ /// Default color. Note that `fill` and `stroke` have higher precedence than `color` and
3674+ /// will override `color`.
3675+ ///
3676+ /// __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
3677+ ///
3678+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
3679+ let color: String?
3680+ /// The mouse cursor used over the mark. Any valid [CSS cursor
3681+ /// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
3682+ let cursor: Cursor?
3683+ /// The horizontal offset, in pixels, between the text label and its anchor point. The offset
3684+ /// is applied after rotation by the _angle_ property.
3685+ let dx: Double?
3686+ /// The vertical offset, in pixels, between the text label and its anchor point. The offset
3687+ /// is applied after rotation by the _angle_ property.
3688+ let dy: Double?
3689+ /// Default Fill Color. This has higher precedence than config.color
3690+ ///
3691+ /// __Default value:__ (None)
3692+ let fill: String?
3693+ /// Whether the mark's color should be used as fill color instead of stroke color.
3694+ ///
3695+ /// __Default value:__ `true` for all marks except `point` and `false` for `point`.
3696+ ///
3697+ /// __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
3698+ ///
3699+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
3700+ let filled: Bool?
3701+ /// The fill opacity (value between [0,1]).
3702+ ///
3703+ /// __Default value:__ `1`
3704+ let fillOpacity: Double?
3705+ /// The typeface to set the text in (e.g., `"Helvetica Neue"`).
3706+ let font: String?
3707+ /// The font size, in pixels.
3708+ let fontSize: Double?
3709+ /// The font style (e.g., `"italic"`).
3710+ let fontStyle: FontStyle?
3711+ /// The font weight (e.g., `"bold"`).
3712+ let fontWeight: FontWeightUnion?
3713+ /// A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
3714+ let href: String?
3715+ /// The line interpolation method to use for line and area marks. One of the following:
3716+ /// - `"linear"`: piecewise linear segments, as in a polyline.
3717+ /// - `"linear-closed"`: close the linear segments to form a polygon.
3718+ /// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
3719+ /// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
3720+ /// function.
3721+ /// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
3722+ /// function.
3723+ /// - `"basis"`: a B-spline, with control point duplication on the ends.
3724+ /// - `"basis-open"`: an open B-spline; may not intersect the start or end.
3725+ /// - `"basis-closed"`: a closed B-spline, as in a loop.
3726+ /// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
3727+ /// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
3728+ /// will intersect other control points.
3729+ /// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
3730+ /// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
3731+ /// spline.
3732+ /// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
3733+ let interpolate: Interpolate?
3734+ /// The maximum length of the text mark in pixels (default 0, indicating no limit). The text
3735+ /// value will be automatically truncated if the rendered size exceeds the limit.
3736+ let limit: Double?
3737+ /// The overall opacity (value between [0,1]).
3738+ ///
3739+ /// __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
3740+ /// `square` marks or layered `bar` charts and `1` otherwise.
3741+ let opacity: Double?
3742+ /// The orientation of a non-stacked bar, tick, area, and line charts.
3743+ /// The value is either horizontal (default) or vertical.
3744+ /// - For bar, rule and tick, this determines whether the size of the bar and tick
3745+ /// should be applied to x or y dimension.
3746+ /// - For area, this property determines the orient property of the Vega output.
3747+ /// - For line, this property determines the sort order of the points in the line
3748+ /// if `config.sortLineBy` is not specified.
3749+ /// For stacked charts, this is always determined by the orientation of the stack;
3750+ /// therefore explicitly specified value will be ignored.
3751+ let orient: Orient?
3752+ /// Polar coordinate radial offset, in pixels, of the text label from the origin determined
3753+ /// by the `x` and `y` properties.
3754+ let radius: Double?
3755+ /// The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
3756+ /// `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
3757+ ///
3758+ /// __Default value:__ `"circle"`
3759+ let shape: String?
3760+ /// Whether month names and weekday names should be abbreviated.
3761+ let shortTimeLabels: Bool?
3762+ /// The pixel area each the point/circle/square.
3763+ /// For example: in the case of circles, the radius is determined in part by the square root
3764+ /// of the size value.
3765+ ///
3766+ /// __Default value:__ `30`
3767+ let size: Double?
3768+ /// Default Stroke Color. This has higher precedence than config.color
3769+ ///
3770+ /// __Default value:__ (None)
3771+ let stroke: String?
3772+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
3773+ let strokeDash: [Double]?
3774+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
3775+ let strokeDashOffset: Double?
3776+ /// The stroke opacity (value between [0,1]).
3777+ ///
3778+ /// __Default value:__ `1`
3779+ let strokeOpacity: Double?
3780+ /// The stroke width, in pixels.
3781+ let strokeWidth: Double?
3782+ /// Depending on the interpolation type, sets the tension parameter (for line and area marks).
3783+ let tension: Double?
3784+ /// Placeholder text if the `text` channel is not specified
3785+ let text: String?
3786+ /// Polar coordinate angle, in radians, of the text label from the origin determined by the
3787+ /// `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
3788+ /// `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
3789+ /// indicating "north".
3790+ let theta: Double?
3791+
3792+ enum CodingKeys: String, CodingKey {
3793+ case align = "align"
3794+ case angle = "angle"
3795+ case baseline = "baseline"
3796+ case color = "color"
3797+ case cursor = "cursor"
3798+ case dx = "dx"
3799+ case dy = "dy"
3800+ case fill = "fill"
3801+ case filled = "filled"
3802+ case fillOpacity = "fillOpacity"
3803+ case font = "font"
3804+ case fontSize = "fontSize"
3805+ case fontStyle = "fontStyle"
3806+ case fontWeight = "fontWeight"
3807+ case href = "href"
3808+ case interpolate = "interpolate"
3809+ case limit = "limit"
3810+ case opacity = "opacity"
3811+ case orient = "orient"
3812+ case radius = "radius"
3813+ case shape = "shape"
3814+ case shortTimeLabels = "shortTimeLabels"
3815+ case size = "size"
3816+ case stroke = "stroke"
3817+ case strokeDash = "strokeDash"
3818+ case strokeDashOffset = "strokeDashOffset"
3819+ case strokeOpacity = "strokeOpacity"
3820+ case strokeWidth = "strokeWidth"
3821+ case tension = "tension"
3822+ case text = "text"
3823+ case theta = "theta"
3824+ }
3825+}
3826+
3827+// MARK: TextConfig convenience initializers and mutators
3828+
3829+extension TextConfig {
3830+ init(data: Data) throws {
3831+ self = try newJSONDecoder().decode(TextConfig.self, from: data)
3832+ }
3833+
3834+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
3835+ guard let data = json.data(using: encoding) else {
3836+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
3837+ }
3838+ try self.init(data: data)
3839+ }
3840+
3841+ init(fromURL url: URL) throws {
3842+ try self.init(data: try Data(contentsOf: url))
3843+ }
3844+
3845+ func with(
3846+ align: HorizontalAlign?? = nil,
3847+ angle: Double?? = nil,
3848+ baseline: VerticalAlign?? = nil,
3849+ color: String?? = nil,
3850+ cursor: Cursor?? = nil,
3851+ dx: Double?? = nil,
3852+ dy: Double?? = nil,
3853+ fill: String?? = nil,
3854+ filled: Bool?? = nil,
3855+ fillOpacity: Double?? = nil,
3856+ font: String?? = nil,
3857+ fontSize: Double?? = nil,
3858+ fontStyle: FontStyle?? = nil,
3859+ fontWeight: FontWeightUnion?? = nil,
3860+ href: String?? = nil,
3861+ interpolate: Interpolate?? = nil,
3862+ limit: Double?? = nil,
3863+ opacity: Double?? = nil,
3864+ orient: Orient?? = nil,
3865+ radius: Double?? = nil,
3866+ shape: String?? = nil,
3867+ shortTimeLabels: Bool?? = nil,
3868+ size: Double?? = nil,
3869+ stroke: String?? = nil,
3870+ strokeDash: [Double]?? = nil,
3871+ strokeDashOffset: Double?? = nil,
3872+ strokeOpacity: Double?? = nil,
3873+ strokeWidth: Double?? = nil,
3874+ tension: Double?? = nil,
3875+ text: String?? = nil,
3876+ theta: Double?? = nil
3877+ ) -> TextConfig {
3878+ return TextConfig(
3879+ align: align ?? self.align,
3880+ angle: angle ?? self.angle,
3881+ baseline: baseline ?? self.baseline,
3882+ color: color ?? self.color,
3883+ cursor: cursor ?? self.cursor,
3884+ dx: dx ?? self.dx,
3885+ dy: dy ?? self.dy,
3886+ fill: fill ?? self.fill,
3887+ filled: filled ?? self.filled,
3888+ fillOpacity: fillOpacity ?? self.fillOpacity,
3889+ font: font ?? self.font,
3890+ fontSize: fontSize ?? self.fontSize,
3891+ fontStyle: fontStyle ?? self.fontStyle,
3892+ fontWeight: fontWeight ?? self.fontWeight,
3893+ href: href ?? self.href,
3894+ interpolate: interpolate ?? self.interpolate,
3895+ limit: limit ?? self.limit,
3896+ opacity: opacity ?? self.opacity,
3897+ orient: orient ?? self.orient,
3898+ radius: radius ?? self.radius,
3899+ shape: shape ?? self.shape,
3900+ shortTimeLabels: shortTimeLabels ?? self.shortTimeLabels,
3901+ size: size ?? self.size,
3902+ stroke: stroke ?? self.stroke,
3903+ strokeDash: strokeDash ?? self.strokeDash,
3904+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
3905+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
3906+ strokeWidth: strokeWidth ?? self.strokeWidth,
3907+ tension: tension ?? self.tension,
3908+ text: text ?? self.text,
3909+ theta: theta ?? self.theta
3910+ )
3911+ }
3912+
3913+ func jsonData() throws -> Data {
3914+ return try newJSONEncoder().encode(self)
3915+ }
3916+
3917+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
3918+ return String(data: try self.jsonData(), encoding: encoding)
3919+ }
3920+}
3921+
3922+/// Tick-Specific Config
3923+// MARK: - TickConfig
3924+struct TickConfig: Codable {
3925+ /// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
3926+ let align: HorizontalAlign?
3927+ /// The rotation angle of the text, in degrees.
3928+ let angle: Double?
3929+ /// The width of the ticks.
3930+ ///
3931+ /// __Default value:__ 2/3 of rangeStep.
3932+ let bandSize: Double?
3933+ /// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
3934+ ///
3935+ /// __Default value:__ `"middle"`
3936+ let baseline: VerticalAlign?
3937+ /// Default color. Note that `fill` and `stroke` have higher precedence than `color` and
3938+ /// will override `color`.
3939+ ///
3940+ /// __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
3941+ ///
3942+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
3943+ let color: String?
3944+ /// The mouse cursor used over the mark. Any valid [CSS cursor
3945+ /// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
3946+ let cursor: Cursor?
3947+ /// The horizontal offset, in pixels, between the text label and its anchor point. The offset
3948+ /// is applied after rotation by the _angle_ property.
3949+ let dx: Double?
3950+ /// The vertical offset, in pixels, between the text label and its anchor point. The offset
3951+ /// is applied after rotation by the _angle_ property.
3952+ let dy: Double?
3953+ /// Default Fill Color. This has higher precedence than config.color
3954+ ///
3955+ /// __Default value:__ (None)
3956+ let fill: String?
3957+ /// Whether the mark's color should be used as fill color instead of stroke color.
3958+ ///
3959+ /// __Default value:__ `true` for all marks except `point` and `false` for `point`.
3960+ ///
3961+ /// __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
3962+ ///
3963+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
3964+ let filled: Bool?
3965+ /// The fill opacity (value between [0,1]).
3966+ ///
3967+ /// __Default value:__ `1`
3968+ let fillOpacity: Double?
3969+ /// The typeface to set the text in (e.g., `"Helvetica Neue"`).
3970+ let font: String?
3971+ /// The font size, in pixels.
3972+ let fontSize: Double?
3973+ /// The font style (e.g., `"italic"`).
3974+ let fontStyle: FontStyle?
3975+ /// The font weight (e.g., `"bold"`).
3976+ let fontWeight: FontWeightUnion?
3977+ /// A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
3978+ let href: String?
3979+ /// The line interpolation method to use for line and area marks. One of the following:
3980+ /// - `"linear"`: piecewise linear segments, as in a polyline.
3981+ /// - `"linear-closed"`: close the linear segments to form a polygon.
3982+ /// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
3983+ /// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
3984+ /// function.
3985+ /// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
3986+ /// function.
3987+ /// - `"basis"`: a B-spline, with control point duplication on the ends.
3988+ /// - `"basis-open"`: an open B-spline; may not intersect the start or end.
3989+ /// - `"basis-closed"`: a closed B-spline, as in a loop.
3990+ /// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
3991+ /// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
3992+ /// will intersect other control points.
3993+ /// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
3994+ /// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
3995+ /// spline.
3996+ /// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
3997+ let interpolate: Interpolate?
3998+ /// The maximum length of the text mark in pixels (default 0, indicating no limit). The text
3999+ /// value will be automatically truncated if the rendered size exceeds the limit.
4000+ let limit: Double?
4001+ /// The overall opacity (value between [0,1]).
4002+ ///
4003+ /// __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
4004+ /// `square` marks or layered `bar` charts and `1` otherwise.
4005+ let opacity: Double?
4006+ /// The orientation of a non-stacked bar, tick, area, and line charts.
4007+ /// The value is either horizontal (default) or vertical.
4008+ /// - For bar, rule and tick, this determines whether the size of the bar and tick
4009+ /// should be applied to x or y dimension.
4010+ /// - For area, this property determines the orient property of the Vega output.
4011+ /// - For line, this property determines the sort order of the points in the line
4012+ /// if `config.sortLineBy` is not specified.
4013+ /// For stacked charts, this is always determined by the orientation of the stack;
4014+ /// therefore explicitly specified value will be ignored.
4015+ let orient: Orient?
4016+ /// Polar coordinate radial offset, in pixels, of the text label from the origin determined
4017+ /// by the `x` and `y` properties.
4018+ let radius: Double?
4019+ /// The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
4020+ /// `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
4021+ ///
4022+ /// __Default value:__ `"circle"`
4023+ let shape: String?
4024+ /// The pixel area each the point/circle/square.
4025+ /// For example: in the case of circles, the radius is determined in part by the square root
4026+ /// of the size value.
4027+ ///
4028+ /// __Default value:__ `30`
4029+ let size: Double?
4030+ /// Default Stroke Color. This has higher precedence than config.color
4031+ ///
4032+ /// __Default value:__ (None)
4033+ let stroke: String?
4034+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
4035+ let strokeDash: [Double]?
4036+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
4037+ let strokeDashOffset: Double?
4038+ /// The stroke opacity (value between [0,1]).
4039+ ///
4040+ /// __Default value:__ `1`
4041+ let strokeOpacity: Double?
4042+ /// The stroke width, in pixels.
4043+ let strokeWidth: Double?
4044+ /// Depending on the interpolation type, sets the tension parameter (for line and area marks).
4045+ let tension: Double?
4046+ /// Placeholder text if the `text` channel is not specified
4047+ let text: String?
4048+ /// Polar coordinate angle, in radians, of the text label from the origin determined by the
4049+ /// `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
4050+ /// `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
4051+ /// indicating "north".
4052+ let theta: Double?
4053+ /// Thickness of the tick mark.
4054+ ///
4055+ /// __Default value:__ `1`
4056+ let thickness: Double?
4057+
4058+ enum CodingKeys: String, CodingKey {
4059+ case align = "align"
4060+ case angle = "angle"
4061+ case bandSize = "bandSize"
4062+ case baseline = "baseline"
4063+ case color = "color"
4064+ case cursor = "cursor"
4065+ case dx = "dx"
4066+ case dy = "dy"
4067+ case fill = "fill"
4068+ case filled = "filled"
4069+ case fillOpacity = "fillOpacity"
4070+ case font = "font"
4071+ case fontSize = "fontSize"
4072+ case fontStyle = "fontStyle"
4073+ case fontWeight = "fontWeight"
4074+ case href = "href"
4075+ case interpolate = "interpolate"
4076+ case limit = "limit"
4077+ case opacity = "opacity"
4078+ case orient = "orient"
4079+ case radius = "radius"
4080+ case shape = "shape"
4081+ case size = "size"
4082+ case stroke = "stroke"
4083+ case strokeDash = "strokeDash"
4084+ case strokeDashOffset = "strokeDashOffset"
4085+ case strokeOpacity = "strokeOpacity"
4086+ case strokeWidth = "strokeWidth"
4087+ case tension = "tension"
4088+ case text = "text"
4089+ case theta = "theta"
4090+ case thickness = "thickness"
4091+ }
4092+}
4093+
4094+// MARK: TickConfig convenience initializers and mutators
4095+
4096+extension TickConfig {
4097+ init(data: Data) throws {
4098+ self = try newJSONDecoder().decode(TickConfig.self, from: data)
4099+ }
4100+
4101+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
4102+ guard let data = json.data(using: encoding) else {
4103+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
4104+ }
4105+ try self.init(data: data)
4106+ }
4107+
4108+ init(fromURL url: URL) throws {
4109+ try self.init(data: try Data(contentsOf: url))
4110+ }
4111+
4112+ func with(
4113+ align: HorizontalAlign?? = nil,
4114+ angle: Double?? = nil,
4115+ bandSize: Double?? = nil,
4116+ baseline: VerticalAlign?? = nil,
4117+ color: String?? = nil,
4118+ cursor: Cursor?? = nil,
4119+ dx: Double?? = nil,
4120+ dy: Double?? = nil,
4121+ fill: String?? = nil,
4122+ filled: Bool?? = nil,
4123+ fillOpacity: Double?? = nil,
4124+ font: String?? = nil,
4125+ fontSize: Double?? = nil,
4126+ fontStyle: FontStyle?? = nil,
4127+ fontWeight: FontWeightUnion?? = nil,
4128+ href: String?? = nil,
4129+ interpolate: Interpolate?? = nil,
4130+ limit: Double?? = nil,
4131+ opacity: Double?? = nil,
4132+ orient: Orient?? = nil,
4133+ radius: Double?? = nil,
4134+ shape: String?? = nil,
4135+ size: Double?? = nil,
4136+ stroke: String?? = nil,
4137+ strokeDash: [Double]?? = nil,
4138+ strokeDashOffset: Double?? = nil,
4139+ strokeOpacity: Double?? = nil,
4140+ strokeWidth: Double?? = nil,
4141+ tension: Double?? = nil,
4142+ text: String?? = nil,
4143+ theta: Double?? = nil,
4144+ thickness: Double?? = nil
4145+ ) -> TickConfig {
4146+ return TickConfig(
4147+ align: align ?? self.align,
4148+ angle: angle ?? self.angle,
4149+ bandSize: bandSize ?? self.bandSize,
4150+ baseline: baseline ?? self.baseline,
4151+ color: color ?? self.color,
4152+ cursor: cursor ?? self.cursor,
4153+ dx: dx ?? self.dx,
4154+ dy: dy ?? self.dy,
4155+ fill: fill ?? self.fill,
4156+ filled: filled ?? self.filled,
4157+ fillOpacity: fillOpacity ?? self.fillOpacity,
4158+ font: font ?? self.font,
4159+ fontSize: fontSize ?? self.fontSize,
4160+ fontStyle: fontStyle ?? self.fontStyle,
4161+ fontWeight: fontWeight ?? self.fontWeight,
4162+ href: href ?? self.href,
4163+ interpolate: interpolate ?? self.interpolate,
4164+ limit: limit ?? self.limit,
4165+ opacity: opacity ?? self.opacity,
4166+ orient: orient ?? self.orient,
4167+ radius: radius ?? self.radius,
4168+ shape: shape ?? self.shape,
4169+ size: size ?? self.size,
4170+ stroke: stroke ?? self.stroke,
4171+ strokeDash: strokeDash ?? self.strokeDash,
4172+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
4173+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
4174+ strokeWidth: strokeWidth ?? self.strokeWidth,
4175+ tension: tension ?? self.tension,
4176+ text: text ?? self.text,
4177+ theta: theta ?? self.theta,
4178+ thickness: thickness ?? self.thickness
4179+ )
4180+ }
4181+
4182+ func jsonData() throws -> Data {
4183+ return try newJSONEncoder().encode(self)
4184+ }
4185+
4186+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
4187+ return String(data: try self.jsonData(), encoding: encoding)
4188+ }
4189+}
4190+
4191+/// Title configuration, which determines default properties for all [titles](title.html).
4192+/// For a full list of title configuration options, please see the [corresponding section of
4193+/// the title documentation](title.html#config).
4194+// MARK: - VGTitleConfig
4195+struct VGTitleConfig: Codable {
4196+ /// The anchor position for placing the title. One of `"start"`, `"middle"`, or `"end"`. For
4197+ /// example, with an orientation of top these anchor positions map to a left-, center-, or
4198+ /// right-aligned title.
4199+ ///
4200+ /// __Default value:__ `"middle"` for [single](spec.html) and [layered](layer.html) views.
4201+ /// `"start"` for other composite views.
4202+ ///
4203+ /// __Note:__ [For now](https://github.com/vega/vega-lite/issues/2875), `anchor` is only
4204+ /// customizable only for [single](spec.html) and [layered](layer.html) views. For other
4205+ /// composite views, `anchor` is always `"start"`.
4206+ let anchor: Anchor?
4207+ /// Angle in degrees of title text.
4208+ let angle: Double?
4209+ /// Vertical text baseline for title text.
4210+ let baseline: VerticalAlign?
4211+ /// Text color for title text.
4212+ let color: String?
4213+ /// Font name for title text.
4214+ let font: String?
4215+ /// Font size in pixels for title text.
4216+ ///
4217+ /// __Default value:__ `10`.
4218+ let fontSize: Double?
4219+ /// Font weight for title text.
4220+ let fontWeight: FontWeightUnion?
4221+ /// The maximum allowed length in pixels of legend labels.
4222+ let limit: Double?
4223+ /// Offset in pixels of the title from the chart body and axes.
4224+ let offset: Double?
4225+ /// Default title orientation ("top", "bottom", "left", or "right")
4226+ let orient: TitleOrient?
4227+
4228+ enum CodingKeys: String, CodingKey {
4229+ case anchor = "anchor"
4230+ case angle = "angle"
4231+ case baseline = "baseline"
4232+ case color = "color"
4233+ case font = "font"
4234+ case fontSize = "fontSize"
4235+ case fontWeight = "fontWeight"
4236+ case limit = "limit"
4237+ case offset = "offset"
4238+ case orient = "orient"
4239+ }
4240+}
4241+
4242+// MARK: VGTitleConfig convenience initializers and mutators
4243+
4244+extension VGTitleConfig {
4245+ init(data: Data) throws {
4246+ self = try newJSONDecoder().decode(VGTitleConfig.self, from: data)
4247+ }
4248+
4249+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
4250+ guard let data = json.data(using: encoding) else {
4251+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
4252+ }
4253+ try self.init(data: data)
4254+ }
4255+
4256+ init(fromURL url: URL) throws {
4257+ try self.init(data: try Data(contentsOf: url))
4258+ }
4259+
4260+ func with(
4261+ anchor: Anchor?? = nil,
4262+ angle: Double?? = nil,
4263+ baseline: VerticalAlign?? = nil,
4264+ color: String?? = nil,
4265+ font: String?? = nil,
4266+ fontSize: Double?? = nil,
4267+ fontWeight: FontWeightUnion?? = nil,
4268+ limit: Double?? = nil,
4269+ offset: Double?? = nil,
4270+ orient: TitleOrient?? = nil
4271+ ) -> VGTitleConfig {
4272+ return VGTitleConfig(
4273+ anchor: anchor ?? self.anchor,
4274+ angle: angle ?? self.angle,
4275+ baseline: baseline ?? self.baseline,
4276+ color: color ?? self.color,
4277+ font: font ?? self.font,
4278+ fontSize: fontSize ?? self.fontSize,
4279+ fontWeight: fontWeight ?? self.fontWeight,
4280+ limit: limit ?? self.limit,
4281+ offset: offset ?? self.offset,
4282+ orient: orient ?? self.orient
4283+ )
4284+ }
4285+
4286+ func jsonData() throws -> Data {
4287+ return try newJSONEncoder().encode(self)
4288+ }
4289+
4290+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
4291+ return String(data: try self.jsonData(), encoding: encoding)
4292+ }
4293+}
4294+
4295+/// The anchor position for placing the title. One of `"start"`, `"middle"`, or `"end"`. For
4296+/// example, with an orientation of top these anchor positions map to a left-, center-, or
4297+/// right-aligned title.
4298+///
4299+/// __Default value:__ `"middle"` for [single](spec.html) and [layered](layer.html) views.
4300+/// `"start"` for other composite views.
4301+///
4302+/// __Note:__ [For now](https://github.com/vega/vega-lite/issues/2875), `anchor` is only
4303+/// customizable only for [single](spec.html) and [layered](layer.html) views. For other
4304+/// composite views, `anchor` is always `"start"`.
4305+enum Anchor: String, Codable {
4306+ case start = "start"
4307+ case middle = "middle"
4308+ case end = "end"
4309+}
4310+
4311+/// Default title orientation ("top", "bottom", "left", or "right")
4312+///
4313+/// The orientation of the title relative to the chart. One of `"top"` (the default),
4314+/// `"bottom"`, `"left"`, or `"right"`.
4315+///
4316+/// The orientation of the axis. One of `"top"`, `"bottom"`, `"left"` or `"right"`. The
4317+/// orientation can be used to further specialize the axis type (e.g., a y axis oriented for
4318+/// the right edge of the chart).
4319+///
4320+/// __Default value:__ `"bottom"` for x-axes and `"left"` for y-axes.
4321+enum TitleOrient: String, Codable {
4322+ case top = "top"
4323+ case bottom = "bottom"
4324+ case orientLeft = "left"
4325+ case orientRight = "right"
4326+}
4327+
4328+/// Default properties for [single view plots](spec.html#single).
4329+// MARK: - ViewConfig
4330+struct ViewConfig: Codable {
4331+ /// Whether the view should be clipped.
4332+ let clip: Bool?
4333+ /// The fill color.
4334+ ///
4335+ /// __Default value:__ (none)
4336+ let fill: String?
4337+ /// The fill opacity (value between [0,1]).
4338+ ///
4339+ /// __Default value:__ (none)
4340+ let fillOpacity: Double?
4341+ /// The default height of the single plot or each plot in a trellis plot when the
4342+ /// visualization has a continuous (non-ordinal) y-scale with `rangeStep` = `null`.
4343+ ///
4344+ /// __Default value:__ `200`
4345+ let height: Double?
4346+ /// The stroke color.
4347+ ///
4348+ /// __Default value:__ (none)
4349+ let stroke: String?
4350+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
4351+ ///
4352+ /// __Default value:__ (none)
4353+ let strokeDash: [Double]?
4354+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
4355+ ///
4356+ /// __Default value:__ (none)
4357+ let strokeDashOffset: Double?
4358+ /// The stroke opacity (value between [0,1]).
4359+ ///
4360+ /// __Default value:__ (none)
4361+ let strokeOpacity: Double?
4362+ /// The stroke width, in pixels.
4363+ ///
4364+ /// __Default value:__ (none)
4365+ let strokeWidth: Double?
4366+ /// The default width of the single plot or each plot in a trellis plot when the
4367+ /// visualization has a continuous (non-ordinal) x-scale or ordinal x-scale with `rangeStep`
4368+ /// = `null`.
4369+ ///
4370+ /// __Default value:__ `200`
4371+ let width: Double?
4372+
4373+ enum CodingKeys: String, CodingKey {
4374+ case clip = "clip"
4375+ case fill = "fill"
4376+ case fillOpacity = "fillOpacity"
4377+ case height = "height"
4378+ case stroke = "stroke"
4379+ case strokeDash = "strokeDash"
4380+ case strokeDashOffset = "strokeDashOffset"
4381+ case strokeOpacity = "strokeOpacity"
4382+ case strokeWidth = "strokeWidth"
4383+ case width = "width"
4384+ }
4385+}
4386+
4387+// MARK: ViewConfig convenience initializers and mutators
4388+
4389+extension ViewConfig {
4390+ init(data: Data) throws {
4391+ self = try newJSONDecoder().decode(ViewConfig.self, from: data)
4392+ }
4393+
4394+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
4395+ guard let data = json.data(using: encoding) else {
4396+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
4397+ }
4398+ try self.init(data: data)
4399+ }
4400+
4401+ init(fromURL url: URL) throws {
4402+ try self.init(data: try Data(contentsOf: url))
4403+ }
4404+
4405+ func with(
4406+ clip: Bool?? = nil,
4407+ fill: String?? = nil,
4408+ fillOpacity: Double?? = nil,
4409+ height: Double?? = nil,
4410+ stroke: String?? = nil,
4411+ strokeDash: [Double]?? = nil,
4412+ strokeDashOffset: Double?? = nil,
4413+ strokeOpacity: Double?? = nil,
4414+ strokeWidth: Double?? = nil,
4415+ width: Double?? = nil
4416+ ) -> ViewConfig {
4417+ return ViewConfig(
4418+ clip: clip ?? self.clip,
4419+ fill: fill ?? self.fill,
4420+ fillOpacity: fillOpacity ?? self.fillOpacity,
4421+ height: height ?? self.height,
4422+ stroke: stroke ?? self.stroke,
4423+ strokeDash: strokeDash ?? self.strokeDash,
4424+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
4425+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
4426+ strokeWidth: strokeWidth ?? self.strokeWidth,
4427+ width: width ?? self.width
4428+ )
4429+ }
4430+
4431+ func jsonData() throws -> Data {
4432+ return try newJSONEncoder().encode(self)
4433+ }
4434+
4435+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
4436+ return String(data: try self.jsonData(), encoding: encoding)
4437+ }
4438+}
4439+
4440+/// An object describing the data source
4441+///
4442+/// Secondary data source to lookup in.
4443+// MARK: - DataClass
4444+struct DataClass: Codable {
4445+ /// An object that specifies the format for parsing the data file.
4446+ ///
4447+ /// An object that specifies the format for parsing the data values.
4448+ ///
4449+ /// An object that specifies the format for parsing the data.
4450+ let format: DataFormat?
4451+ /// An URL from which to load the data set. Use the `format.type` property
4452+ /// to ensure the loaded data is correctly parsed.
4453+ let url: String?
4454+ /// The full data set, included inline. This can be an array of objects or primitive values
4455+ /// or a string.
4456+ /// Arrays of primitive values are ingested as objects with a `data` property. Strings are
4457+ /// parsed according to the specified format type.
4458+ let values: Values?
4459+ /// Provide a placeholder name and bind data at runtime.
4460+ let name: String?
4461+
4462+ enum CodingKeys: String, CodingKey {
4463+ case format = "format"
4464+ case url = "url"
4465+ case values = "values"
4466+ case name = "name"
4467+ }
4468+}
4469+
4470+// MARK: DataClass convenience initializers and mutators
4471+
4472+extension DataClass {
4473+ init(data: Data) throws {
4474+ self = try newJSONDecoder().decode(DataClass.self, from: data)
4475+ }
4476+
4477+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
4478+ guard let data = json.data(using: encoding) else {
4479+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
4480+ }
4481+ try self.init(data: data)
4482+ }
4483+
4484+ init(fromURL url: URL) throws {
4485+ try self.init(data: try Data(contentsOf: url))
4486+ }
4487+
4488+ func with(
4489+ format: DataFormat?? = nil,
4490+ url: String?? = nil,
4491+ values: Values?? = nil,
4492+ name: String?? = nil
4493+ ) -> DataClass {
4494+ return DataClass(
4495+ format: format ?? self.format,
4496+ url: url ?? self.url,
4497+ values: values ?? self.values,
4498+ name: name ?? self.name
4499+ )
4500+ }
4501+
4502+ func jsonData() throws -> Data {
4503+ return try newJSONEncoder().encode(self)
4504+ }
4505+
4506+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
4507+ return String(data: try self.jsonData(), encoding: encoding)
4508+ }
4509+}
4510+
4511+/// An object that specifies the format for parsing the data file.
4512+///
4513+/// An object that specifies the format for parsing the data values.
4514+///
4515+/// An object that specifies the format for parsing the data.
4516+// MARK: - DataFormat
4517+struct DataFormat: Codable {
4518+ /// If set to auto (the default), perform automatic type inference to determine the desired
4519+ /// data types.
4520+ /// Alternatively, a parsing directive object can be provided for explicit data types. Each
4521+ /// property of the object corresponds to a field name, and the value to the desired data
4522+ /// type (one of `"number"`, `"boolean"` or `"date"`).
4523+ /// For example, `"parse": {"modified_on": "date"}` parses the `modified_on` field in each
4524+ /// input record a Date value.
4525+ ///
4526+ /// For `"date"`, we parse data based using Javascript's
4527+ /// [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
4528+ /// For Specific date formats can be provided (e.g., `{foo: 'date:"%m%d%Y"'}`), using the
4529+ /// [d3-time-format syntax](https://github.com/d3/d3-time-format#locale_format). UTC date
4530+ /// format parsing is supported similarly (e.g., `{foo: 'utc:"%m%d%Y"'}`). See more about
4531+ /// [UTC time](timeunit.html#utc)
4532+ let parse: ParseUnion?
4533+ /// Type of input data: `"json"`, `"csv"`, `"tsv"`.
4534+ /// The default format type is determined by the extension of the file URL.
4535+ /// If no extension is detected, `"json"` will be used by default.
4536+ let type: DataFormatType?
4537+ /// The JSON property containing the desired data.
4538+ /// This parameter can be used when the loaded JSON file may have surrounding structure or
4539+ /// meta-data.
4540+ /// For example `"property": "values.features"` is equivalent to retrieving
4541+ /// `json.values.features`
4542+ /// from the loaded JSON object.
4543+ let property: String?
4544+ /// The name of the TopoJSON object set to convert to a GeoJSON feature collection.
4545+ /// For example, in a map of the world, there may be an object set named `"countries"`.
4546+ /// Using the feature property, we can extract this set and generate a GeoJSON feature object
4547+ /// for each country.
4548+ let feature: String?
4549+ /// The name of the TopoJSON object set to convert to mesh.
4550+ /// Similar to the `feature` option, `mesh` extracts a named TopoJSON object set.
4551+ /// Unlike the `feature` option, the corresponding geo data is returned as a single, unified
4552+ /// mesh instance, not as individual GeoJSON features.
4553+ /// Extracting a mesh is useful for more efficiently drawing borders or other geographic
4554+ /// elements that you do not need to associate with specific regions such as individual
4555+ /// countries, states or counties.
4556+ let mesh: String?
4557+
4558+ enum CodingKeys: String, CodingKey {
4559+ case parse = "parse"
4560+ case type = "type"
4561+ case property = "property"
4562+ case feature = "feature"
4563+ case mesh = "mesh"
4564+ }
4565+}
4566+
4567+// MARK: DataFormat convenience initializers and mutators
4568+
4569+extension DataFormat {
4570+ init(data: Data) throws {
4571+ self = try newJSONDecoder().decode(DataFormat.self, from: data)
4572+ }
4573+
4574+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
4575+ guard let data = json.data(using: encoding) else {
4576+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
4577+ }
4578+ try self.init(data: data)
4579+ }
4580+
4581+ init(fromURL url: URL) throws {
4582+ try self.init(data: try Data(contentsOf: url))
4583+ }
4584+
4585+ func with(
4586+ parse: ParseUnion?? = nil,
4587+ type: DataFormatType?? = nil,
4588+ property: String?? = nil,
4589+ feature: String?? = nil,
4590+ mesh: String?? = nil
4591+ ) -> DataFormat {
4592+ return DataFormat(
4593+ parse: parse ?? self.parse,
4594+ type: type ?? self.type,
4595+ property: property ?? self.property,
4596+ feature: feature ?? self.feature,
4597+ mesh: mesh ?? self.mesh
4598+ )
4599+ }
4600+
4601+ func jsonData() throws -> Data {
4602+ return try newJSONEncoder().encode(self)
4603+ }
4604+
4605+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
4606+ return String(data: try self.jsonData(), encoding: encoding)
4607+ }
4608+}
4609+
4610+enum ParseUnion: Codable {
4611+ case anythingMap([String: JSONAny])
4612+ case enumeration(ParseEnum)
4613+
4614+ init(from decoder: Decoder) throws {
4615+ let container = try decoder.singleValueContainer()
4616+ if let x = try? container.decode(ParseEnum.self) {
4617+ self = .enumeration(x)
4618+ return
4619+ }
4620+ if let x = try? container.decode([String: JSONAny].self) {
4621+ self = .anythingMap(x)
4622+ return
4623+ }
4624+ throw DecodingError.typeMismatch(ParseUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ParseUnion"))
4625+ }
4626+
4627+ func encode(to encoder: Encoder) throws {
4628+ var container = encoder.singleValueContainer()
4629+ switch self {
4630+ case .anythingMap(let x):
4631+ try container.encode(x)
4632+ case .enumeration(let x):
4633+ try container.encode(x)
4634+ }
4635+ }
4636+}
4637+
4638+enum ParseEnum: String, Codable {
4639+ case auto = "auto"
4640+}
4641+
4642+/// Type of input data: `"json"`, `"csv"`, `"tsv"`.
4643+/// The default format type is determined by the extension of the file URL.
4644+/// If no extension is detected, `"json"` will be used by default.
4645+enum DataFormatType: String, Codable {
4646+ case csv = "csv"
4647+ case tsv = "tsv"
4648+ case json = "json"
4649+ case topojson = "topojson"
4650+}
4651+
4652+/// The full data set, included inline. This can be an array of objects or primitive values
4653+/// or a string.
4654+/// Arrays of primitive values are ingested as objects with a `data` property. Strings are
4655+/// parsed according to the specified format type.
4656+enum Values: Codable {
4657+ case anythingMap([String: JSONAny])
4658+ case string(String)
4659+ case unionArray([ValuesValue])
4660+
4661+ init(from decoder: Decoder) throws {
4662+ let container = try decoder.singleValueContainer()
4663+ if let x = try? container.decode([ValuesValue].self) {
4664+ self = .unionArray(x)
4665+ return
4666+ }
4667+ if let x = try? container.decode([String: JSONAny].self) {
4668+ self = .anythingMap(x)
4669+ return
4670+ }
4671+ if let x = try? container.decode(String.self) {
4672+ self = .string(x)
4673+ return
4674+ }
4675+ throw DecodingError.typeMismatch(Values.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Values"))
4676+ }
4677+
4678+ func encode(to encoder: Encoder) throws {
4679+ var container = encoder.singleValueContainer()
4680+ switch self {
4681+ case .anythingMap(let x):
4682+ try container.encode(x)
4683+ case .string(let x):
4684+ try container.encode(x)
4685+ case .unionArray(let x):
4686+ try container.encode(x)
4687+ }
4688+ }
4689+}
4690+
4691+enum ValuesValue: Codable {
4692+ case anythingMap([String: JSONAny])
4693+ case bool(Bool)
4694+ case double(Double)
4695+ case string(String)
4696+
4697+ init(from decoder: Decoder) throws {
4698+ let container = try decoder.singleValueContainer()
4699+ if let x = try? container.decode(Bool.self) {
4700+ self = .bool(x)
4701+ return
4702+ }
4703+ if let x = try? container.decode(Double.self) {
4704+ self = .double(x)
4705+ return
4706+ }
4707+ if let x = try? container.decode([String: JSONAny].self) {
4708+ self = .anythingMap(x)
4709+ return
4710+ }
4711+ if let x = try? container.decode(String.self) {
4712+ self = .string(x)
4713+ return
4714+ }
4715+ throw DecodingError.typeMismatch(ValuesValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ValuesValue"))
4716+ }
4717+
4718+ func encode(to encoder: Encoder) throws {
4719+ var container = encoder.singleValueContainer()
4720+ switch self {
4721+ case .anythingMap(let x):
4722+ try container.encode(x)
4723+ case .bool(let x):
4724+ try container.encode(x)
4725+ case .double(let x):
4726+ try container.encode(x)
4727+ case .string(let x):
4728+ try container.encode(x)
4729+ }
4730+ }
4731+}
4732+
4733+/// A key-value mapping between encoding channels and definition of fields.
4734+// MARK: - EncodingWithFacet
4735+struct EncodingWithFacet: Codable {
4736+ /// Color of the marks – either fill or stroke color based on mark type.
4737+ /// By default, `color` represents fill color for `"area"`, `"bar"`, `"tick"`,
4738+ /// `"text"`, `"circle"`, and `"square"` / stroke color for `"line"` and `"point"`.
4739+ ///
4740+ /// __Default value:__ If undefined, the default color depends on [mark
4741+ /// config](config.html#mark)'s `color` property.
4742+ ///
4743+ /// _Note:_ See the scale documentation for more information about customizing [color
4744+ /// scheme](scale.html#scheme).
4745+ let color: MarkPropDefWithCondition?
4746+ /// Horizontal facets for trellis plots.
4747+ let column: FacetFieldDef?
4748+ /// Additional levels of detail for grouping data in aggregate views and
4749+ /// in line and area marks without mapping data to a specific visual channel.
4750+ let detail: Detail?
4751+ /// A URL to load upon mouse click.
4752+ let href: DefWithCondition?
4753+ /// Opacity of the marks – either can be a value or a range.
4754+ ///
4755+ /// __Default value:__ If undefined, the default opacity depends on [mark
4756+ /// config](config.html#mark)'s `opacity` property.
4757+ let opacity: MarkPropDefWithCondition?
4758+ /// Stack order for stacked marks or order of data points in line marks for connected scatter
4759+ /// plots.
4760+ ///
4761+ /// __Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating
4762+ /// additional aggregation grouping.
4763+ let order: Order?
4764+ /// Vertical facets for trellis plots.
4765+ let row: FacetFieldDef?
4766+ /// For `point` marks the supported values are
4767+ /// `"circle"` (default), `"square"`, `"cross"`, `"diamond"`, `"triangle-up"`,
4768+ /// or `"triangle-down"`, or else a custom SVG path string.
4769+ /// For `geoshape` marks it should be a field definition of the geojson data
4770+ ///
4771+ /// __Default value:__ If undefined, the default shape depends on [mark
4772+ /// config](config.html#point-config)'s `shape` property.
4773+ let shape: MarkPropDefWithCondition?
4774+ /// Size of the mark.
4775+ /// - For `"point"`, `"square"` and `"circle"`, – the symbol size, or pixel area of the mark.
4776+ /// - For `"bar"` and `"tick"` – the bar and tick's size.
4777+ /// - For `"text"` – the text's font size.
4778+ /// - Size is currently unsupported for `"line"`, `"area"`, and `"rect"`.
4779+ let size: MarkPropDefWithCondition?
4780+ /// Text of the `text` mark.
4781+ let text: TextDefWithCondition?
4782+ /// The tooltip text to show upon mouse hover.
4783+ let tooltip: TextDefWithCondition?
4784+ /// X coordinates of the marks, or width of horizontal `"bar"` and `"area"`.
4785+ let x: XClass?
4786+ /// X2 coordinates for ranged `"area"`, `"bar"`, `"rect"`, and `"rule"`.
4787+ let x2: X2Class?
4788+ /// Y coordinates of the marks, or height of vertical `"bar"` and `"area"`.
4789+ let y: XClass?
4790+ /// Y2 coordinates for ranged `"area"`, `"bar"`, `"rect"`, and `"rule"`.
4791+ let y2: X2Class?
4792+
4793+ enum CodingKeys: String, CodingKey {
4794+ case color = "color"
4795+ case column = "column"
4796+ case detail = "detail"
4797+ case href = "href"
4798+ case opacity = "opacity"
4799+ case order = "order"
4800+ case row = "row"
4801+ case shape = "shape"
4802+ case size = "size"
4803+ case text = "text"
4804+ case tooltip = "tooltip"
4805+ case x = "x"
4806+ case x2 = "x2"
4807+ case y = "y"
4808+ case y2 = "y2"
4809+ }
4810+}
4811+
4812+// MARK: EncodingWithFacet convenience initializers and mutators
4813+
4814+extension EncodingWithFacet {
4815+ init(data: Data) throws {
4816+ self = try newJSONDecoder().decode(EncodingWithFacet.self, from: data)
4817+ }
4818+
4819+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
4820+ guard let data = json.data(using: encoding) else {
4821+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
4822+ }
4823+ try self.init(data: data)
4824+ }
4825+
4826+ init(fromURL url: URL) throws {
4827+ try self.init(data: try Data(contentsOf: url))
4828+ }
4829+
4830+ func with(
4831+ color: MarkPropDefWithCondition?? = nil,
4832+ column: FacetFieldDef?? = nil,
4833+ detail: Detail?? = nil,
4834+ href: DefWithCondition?? = nil,
4835+ opacity: MarkPropDefWithCondition?? = nil,
4836+ order: Order?? = nil,
4837+ row: FacetFieldDef?? = nil,
4838+ shape: MarkPropDefWithCondition?? = nil,
4839+ size: MarkPropDefWithCondition?? = nil,
4840+ text: TextDefWithCondition?? = nil,
4841+ tooltip: TextDefWithCondition?? = nil,
4842+ x: XClass?? = nil,
4843+ x2: X2Class?? = nil,
4844+ y: XClass?? = nil,
4845+ y2: X2Class?? = nil
4846+ ) -> EncodingWithFacet {
4847+ return EncodingWithFacet(
4848+ color: color ?? self.color,
4849+ column: column ?? self.column,
4850+ detail: detail ?? self.detail,
4851+ href: href ?? self.href,
4852+ opacity: opacity ?? self.opacity,
4853+ order: order ?? self.order,
4854+ row: row ?? self.row,
4855+ shape: shape ?? self.shape,
4856+ size: size ?? self.size,
4857+ text: text ?? self.text,
4858+ tooltip: tooltip ?? self.tooltip,
4859+ x: x ?? self.x,
4860+ x2: x2 ?? self.x2,
4861+ y: y ?? self.y,
4862+ y2: y2 ?? self.y2
4863+ )
4864+ }
4865+
4866+ func jsonData() throws -> Data {
4867+ return try newJSONEncoder().encode(self)
4868+ }
4869+
4870+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
4871+ return String(data: try self.jsonData(), encoding: encoding)
4872+ }
4873+}
4874+
4875+/// Color of the marks – either fill or stroke color based on mark type.
4876+/// By default, `color` represents fill color for `"area"`, `"bar"`, `"tick"`,
4877+/// `"text"`, `"circle"`, and `"square"` / stroke color for `"line"` and `"point"`.
4878+///
4879+/// __Default value:__ If undefined, the default color depends on [mark
4880+/// config](config.html#mark)'s `color` property.
4881+///
4882+/// _Note:_ See the scale documentation for more information about customizing [color
4883+/// scheme](scale.html#scheme).
4884+///
4885+/// Opacity of the marks – either can be a value or a range.
4886+///
4887+/// __Default value:__ If undefined, the default opacity depends on [mark
4888+/// config](config.html#mark)'s `opacity` property.
4889+///
4890+/// For `point` marks the supported values are
4891+/// `"circle"` (default), `"square"`, `"cross"`, `"diamond"`, `"triangle-up"`,
4892+/// or `"triangle-down"`, or else a custom SVG path string.
4893+/// For `geoshape` marks it should be a field definition of the geojson data
4894+///
4895+/// __Default value:__ If undefined, the default shape depends on [mark
4896+/// config](config.html#point-config)'s `shape` property.
4897+///
4898+/// Size of the mark.
4899+/// - For `"point"`, `"square"` and `"circle"`, – the symbol size, or pixel area of the mark.
4900+/// - For `"bar"` and `"tick"` – the bar and tick's size.
4901+/// - For `"text"` – the text's font size.
4902+/// - Size is currently unsupported for `"line"`, `"area"`, and `"rect"`.
4903+///
4904+/// A FieldDef with Condition<ValueDef>
4905+/// {
4906+/// condition: {value: ...},
4907+/// field: ...,
4908+/// ...
4909+/// }
4910+///
4911+/// A ValueDef with Condition<ValueDef | FieldDef>
4912+/// {
4913+/// condition: {field: ...} | {value: ...},
4914+/// value: ...,
4915+/// }
4916+// MARK: - MarkPropDefWithCondition
4917+struct MarkPropDefWithCondition: Codable {
4918+ /// Aggregation function for the field
4919+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
4920+ ///
4921+ /// __Default value:__ `undefined` (None)
4922+ let aggregate: AggregateOp?
4923+ /// A flag for binning a `quantitative` field, or [an object defining binning
4924+ /// parameters](bin.html#params).
4925+ /// If `true`, default [binning parameters](bin.html) will be applied.
4926+ ///
4927+ /// __Default value:__ `false`
4928+ let bin: Bin?
4929+ /// One or more value definition(s) with a selection predicate.
4930+ ///
4931+ /// __Note:__ A field definition's `condition` property can only contain [value
4932+ /// definitions](encoding.html#value-def)
4933+ /// since Vega-Lite only allows at most one encoded field per encoding channel.
4934+ ///
4935+ /// A field definition or one or more value definition(s) with a selection predicate.
4936+ let condition: ColorCondition?
4937+ /// __Required.__ A string defining the name of the field from which to pull a data value
4938+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
4939+ ///
4940+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
4941+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
4942+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
4943+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
4944+ /// See more details about escaping in the [field documentation](field.html).
4945+ ///
4946+ /// __Note:__ `field` is not required if `aggregate` is `count`.
4947+ let field: Field?
4948+ /// An object defining properties of the legend.
4949+ /// If `null`, the legend for the encoding channel will be removed.
4950+ ///
4951+ /// __Default value:__ If undefined, default [legend properties](legend.html) are applied.
4952+ let legend: Legend?
4953+ /// An object defining properties of the channel's scale, which is the function that
4954+ /// transforms values in the data domain (numbers, dates, strings, etc) to visual values
4955+ /// (pixels, colors, sizes) of the encoding channels.
4956+ ///
4957+ /// __Default value:__ If undefined, default [scale properties](scale.html) are applied.
4958+ let scale: Scale?
4959+ /// Sort order for the encoded field.
4960+ /// Supported `sort` values include `"ascending"`, `"descending"` and `null` (no sorting).
4961+ /// For fields with discrete domains, `sort` can also be a [sort field definition
4962+ /// object](sort.html#sort-field).
4963+ ///
4964+ /// __Default value:__ `"ascending"`
4965+ let sort: SortUnion?
4966+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
4967+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
4968+ ///
4969+ /// __Default value:__ `undefined` (None)
4970+ let timeUnit: TimeUnit?
4971+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
4972+ /// `"nominal"`).
4973+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
4974+ /// [geographic projection](projection.html) is applied.
4975+ let type: ConditionalPredicateValueDefType?
4976+ /// A constant value in visual domain.
4977+ let value: ConditionalValueDefValue?
4978+
4979+ enum CodingKeys: String, CodingKey {
4980+ case aggregate = "aggregate"
4981+ case bin = "bin"
4982+ case condition = "condition"
4983+ case field = "field"
4984+ case legend = "legend"
4985+ case scale = "scale"
4986+ case sort = "sort"
4987+ case timeUnit = "timeUnit"
4988+ case type = "type"
4989+ case value = "value"
4990+ }
4991+}
4992+
4993+// MARK: MarkPropDefWithCondition convenience initializers and mutators
4994+
4995+extension MarkPropDefWithCondition {
4996+ init(data: Data) throws {
4997+ self = try newJSONDecoder().decode(MarkPropDefWithCondition.self, from: data)
4998+ }
4999+
5000+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5001+ guard let data = json.data(using: encoding) else {
5002+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5003+ }
5004+ try self.init(data: data)
5005+ }
5006+
5007+ init(fromURL url: URL) throws {
5008+ try self.init(data: try Data(contentsOf: url))
5009+ }
5010+
5011+ func with(
5012+ aggregate: AggregateOp?? = nil,
5013+ bin: Bin?? = nil,
5014+ condition: ColorCondition?? = nil,
5015+ field: Field?? = nil,
5016+ legend: Legend?? = nil,
5017+ scale: Scale?? = nil,
5018+ sort: SortUnion?? = nil,
5019+ timeUnit: TimeUnit?? = nil,
5020+ type: ConditionalPredicateValueDefType?? = nil,
5021+ value: ConditionalValueDefValue?? = nil
5022+ ) -> MarkPropDefWithCondition {
5023+ return MarkPropDefWithCondition(
5024+ aggregate: aggregate ?? self.aggregate,
5025+ bin: bin ?? self.bin,
5026+ condition: condition ?? self.condition,
5027+ field: field ?? self.field,
5028+ legend: legend ?? self.legend,
5029+ scale: scale ?? self.scale,
5030+ sort: sort ?? self.sort,
5031+ timeUnit: timeUnit ?? self.timeUnit,
5032+ type: type ?? self.type,
5033+ value: value ?? self.value
5034+ )
5035+ }
5036+
5037+ func jsonData() throws -> Data {
5038+ return try newJSONEncoder().encode(self)
5039+ }
5040+
5041+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5042+ return String(data: try self.jsonData(), encoding: encoding)
5043+ }
5044+}
5045+
5046+/// Aggregation function for the field
5047+/// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
5048+///
5049+/// __Default value:__ `undefined` (None)
5050+///
5051+/// An [aggregate operation](aggregate.html#ops) to perform on the field prior to sorting
5052+/// (e.g., `"count"`, `"mean"` and `"median"`).
5053+/// This property is required in cases where the sort field and the data reference field do
5054+/// not match.
5055+/// The input data objects will be aggregated, grouped by the encoded data field.
5056+///
5057+/// For a full list of operations, please see the documentation for
5058+/// [aggregate](aggregate.html#ops).
5059+///
5060+/// The aggregation operations to apply to the fields, such as sum, average or count.
5061+/// See the [full list of supported aggregation
5062+/// operations](https://vega.github.io/vega-lite/docs/aggregate.html#ops)
5063+/// for more information.
5064+enum AggregateOp: String, Codable {
5065+ case argmax = "argmax"
5066+ case argmin = "argmin"
5067+ case average = "average"
5068+ case count = "count"
5069+ case distinct = "distinct"
5070+ case max = "max"
5071+ case mean = "mean"
5072+ case median = "median"
5073+ case min = "min"
5074+ case missing = "missing"
5075+ case q1 = "q1"
5076+ case q3 = "q3"
5077+ case ci0 = "ci0"
5078+ case ci1 = "ci1"
5079+ case stdev = "stdev"
5080+ case stdevp = "stdevp"
5081+ case sum = "sum"
5082+ case valid = "valid"
5083+ case values = "values"
5084+ case variance = "variance"
5085+ case variancep = "variancep"
5086+}
5087+
5088+enum Bin: Codable {
5089+ case binParams(BinParams)
5090+ case bool(Bool)
5091+
5092+ init(from decoder: Decoder) throws {
5093+ let container = try decoder.singleValueContainer()
5094+ if let x = try? container.decode(Bool.self) {
5095+ self = .bool(x)
5096+ return
5097+ }
5098+ if let x = try? container.decode(BinParams.self) {
5099+ self = .binParams(x)
5100+ return
5101+ }
5102+ throw DecodingError.typeMismatch(Bin.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Bin"))
5103+ }
5104+
5105+ func encode(to encoder: Encoder) throws {
5106+ var container = encoder.singleValueContainer()
5107+ switch self {
5108+ case .binParams(let x):
5109+ try container.encode(x)
5110+ case .bool(let x):
5111+ try container.encode(x)
5112+ }
5113+ }
5114+}
5115+
5116+/// Binning properties or boolean flag for determining whether to bin data or not.
5117+// MARK: - BinParams
5118+struct BinParams: Codable {
5119+ /// The number base to use for automatic bin determination (default is base 10).
5120+ ///
5121+ /// __Default value:__ `10`
5122+ let base: Double?
5123+ /// Scale factors indicating allowable subdivisions. The default value is [5, 2], which
5124+ /// indicates that for base 10 numbers (the default base), the method may consider dividing
5125+ /// bin sizes by 5 and/or 2. For example, for an initial step size of 10, the method can
5126+ /// check if bin sizes of 2 (= 10/5), 5 (= 10/2), or 1 (= 10/(5*2)) might also satisfy the
5127+ /// given constraints.
5128+ ///
5129+ /// __Default value:__ `[5, 2]`
5130+ let divide: [Double]?
5131+ /// A two-element (`[min, max]`) array indicating the range of desired bin values.
5132+ let extent: [Double]?
5133+ /// Maximum number of bins.
5134+ ///
5135+ /// __Default value:__ `6` for `row`, `column` and `shape` channels; `10` for other channels
5136+ let maxbins: Double?
5137+ /// A minimum allowable step size (particularly useful for integer values).
5138+ let minstep: Double?
5139+ /// If true (the default), attempts to make the bin boundaries use human-friendly boundaries,
5140+ /// such as multiples of ten.
5141+ let nice: Bool?
5142+ /// An exact step size to use between bins.
5143+ ///
5144+ /// __Note:__ If provided, options such as maxbins will be ignored.
5145+ let step: Double?
5146+ /// An array of allowable step sizes to choose from.
5147+ let steps: [Double]?
5148+
5149+ enum CodingKeys: String, CodingKey {
5150+ case base = "base"
5151+ case divide = "divide"
5152+ case extent = "extent"
5153+ case maxbins = "maxbins"
5154+ case minstep = "minstep"
5155+ case nice = "nice"
5156+ case step = "step"
5157+ case steps = "steps"
5158+ }
5159+}
5160+
5161+// MARK: BinParams convenience initializers and mutators
5162+
5163+extension BinParams {
5164+ init(data: Data) throws {
5165+ self = try newJSONDecoder().decode(BinParams.self, from: data)
5166+ }
5167+
5168+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5169+ guard let data = json.data(using: encoding) else {
5170+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5171+ }
5172+ try self.init(data: data)
5173+ }
5174+
5175+ init(fromURL url: URL) throws {
5176+ try self.init(data: try Data(contentsOf: url))
5177+ }
5178+
5179+ func with(
5180+ base: Double?? = nil,
5181+ divide: [Double]?? = nil,
5182+ extent: [Double]?? = nil,
5183+ maxbins: Double?? = nil,
5184+ minstep: Double?? = nil,
5185+ nice: Bool?? = nil,
5186+ step: Double?? = nil,
5187+ steps: [Double]?? = nil
5188+ ) -> BinParams {
5189+ return BinParams(
5190+ base: base ?? self.base,
5191+ divide: divide ?? self.divide,
5192+ extent: extent ?? self.extent,
5193+ maxbins: maxbins ?? self.maxbins,
5194+ minstep: minstep ?? self.minstep,
5195+ nice: nice ?? self.nice,
5196+ step: step ?? self.step,
5197+ steps: steps ?? self.steps
5198+ )
5199+ }
5200+
5201+ func jsonData() throws -> Data {
5202+ return try newJSONEncoder().encode(self)
5203+ }
5204+
5205+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5206+ return String(data: try self.jsonData(), encoding: encoding)
5207+ }
5208+}
5209+
5210+enum ColorCondition: Codable {
5211+ case conditionalPredicateMarkPropFieldDefClass(ConditionalPredicateMarkPropFieldDefClass)
5212+ case conditionalValueDefArray([ConditionalValueDef])
5213+
5214+ init(from decoder: Decoder) throws {
5215+ let container = try decoder.singleValueContainer()
5216+ if let x = try? container.decode([ConditionalValueDef].self) {
5217+ self = .conditionalValueDefArray(x)
5218+ return
5219+ }
5220+ if let x = try? container.decode(ConditionalPredicateMarkPropFieldDefClass.self) {
5221+ self = .conditionalPredicateMarkPropFieldDefClass(x)
5222+ return
5223+ }
5224+ throw DecodingError.typeMismatch(ColorCondition.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ColorCondition"))
5225+ }
5226+
5227+ func encode(to encoder: Encoder) throws {
5228+ var container = encoder.singleValueContainer()
5229+ switch self {
5230+ case .conditionalPredicateMarkPropFieldDefClass(let x):
5231+ try container.encode(x)
5232+ case .conditionalValueDefArray(let x):
5233+ try container.encode(x)
5234+ }
5235+ }
5236+}
5237+
5238+// MARK: - ConditionalValueDef
5239+struct ConditionalValueDef: Codable {
5240+ let test: LogicalOperandPredicate?
5241+ /// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
5242+ /// `0` to `1` for opacity).
5243+ let value: ConditionalValueDefValue
5244+ /// A [selection name](selection.html), or a series of [composed
5245+ /// selections](selection.html#compose).
5246+ let selection: SelectionOperand?
5247+
5248+ enum CodingKeys: String, CodingKey {
5249+ case test = "test"
5250+ case value = "value"
5251+ case selection = "selection"
5252+ }
5253+}
5254+
5255+// MARK: ConditionalValueDef convenience initializers and mutators
5256+
5257+extension ConditionalValueDef {
5258+ init(data: Data) throws {
5259+ self = try newJSONDecoder().decode(ConditionalValueDef.self, from: data)
5260+ }
5261+
5262+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5263+ guard let data = json.data(using: encoding) else {
5264+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5265+ }
5266+ try self.init(data: data)
5267+ }
5268+
5269+ init(fromURL url: URL) throws {
5270+ try self.init(data: try Data(contentsOf: url))
5271+ }
5272+
5273+ func with(
5274+ test: LogicalOperandPredicate?? = nil,
5275+ value: ConditionalValueDefValue? = nil,
5276+ selection: SelectionOperand?? = nil
5277+ ) -> ConditionalValueDef {
5278+ return ConditionalValueDef(
5279+ test: test ?? self.test,
5280+ value: value ?? self.value,
5281+ selection: selection ?? self.selection
5282+ )
5283+ }
5284+
5285+ func jsonData() throws -> Data {
5286+ return try newJSONEncoder().encode(self)
5287+ }
5288+
5289+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5290+ return String(data: try self.jsonData(), encoding: encoding)
5291+ }
5292+}
5293+
5294+// MARK: - Selection
5295+struct Selection: Codable {
5296+ let not: SelectionOperand?
5297+ let and: [SelectionOperand]?
5298+ let or: [SelectionOperand]?
5299+
5300+ enum CodingKeys: String, CodingKey {
5301+ case not = "not"
5302+ case and = "and"
5303+ case or = "or"
5304+ }
5305+}
5306+
5307+// MARK: Selection convenience initializers and mutators
5308+
5309+extension Selection {
5310+ init(data: Data) throws {
5311+ self = try newJSONDecoder().decode(Selection.self, from: data)
5312+ }
5313+
5314+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5315+ guard let data = json.data(using: encoding) else {
5316+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5317+ }
5318+ try self.init(data: data)
5319+ }
5320+
5321+ init(fromURL url: URL) throws {
5322+ try self.init(data: try Data(contentsOf: url))
5323+ }
5324+
5325+ func with(
5326+ not: SelectionOperand?? = nil,
5327+ and: [SelectionOperand]?? = nil,
5328+ or: [SelectionOperand]?? = nil
5329+ ) -> Selection {
5330+ return Selection(
5331+ not: not ?? self.not,
5332+ and: and ?? self.and,
5333+ or: or ?? self.or
5334+ )
5335+ }
5336+
5337+ func jsonData() throws -> Data {
5338+ return try newJSONEncoder().encode(self)
5339+ }
5340+
5341+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5342+ return String(data: try self.jsonData(), encoding: encoding)
5343+ }
5344+}
5345+
5346+/// Filter using a selection name.
5347+///
5348+/// A [selection name](selection.html), or a series of [composed
5349+/// selections](selection.html#compose).
5350+indirect enum SelectionOperand: Codable {
5351+ case selection(Selection)
5352+ case string(String)
5353+
5354+ init(from decoder: Decoder) throws {
5355+ let container = try decoder.singleValueContainer()
5356+ if let x = try? container.decode(String.self) {
5357+ self = .string(x)
5358+ return
5359+ }
5360+ if let x = try? container.decode(Selection.self) {
5361+ self = .selection(x)
5362+ return
5363+ }
5364+ throw DecodingError.typeMismatch(SelectionOperand.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for SelectionOperand"))
5365+ }
5366+
5367+ func encode(to encoder: Encoder) throws {
5368+ var container = encoder.singleValueContainer()
5369+ switch self {
5370+ case .selection(let x):
5371+ try container.encode(x)
5372+ case .string(let x):
5373+ try container.encode(x)
5374+ }
5375+ }
5376+}
5377+
5378+// MARK: - Predicate
5379+struct Predicate: Codable {
5380+ let not: LogicalOperandPredicate?
5381+ let and: [LogicalOperandPredicate]?
5382+ let or: [LogicalOperandPredicate]?
5383+ /// The value that the field should be equal to.
5384+ let equal: Equal?
5385+ /// Field to be filtered.
5386+ ///
5387+ /// Field to be filtered
5388+ let field: String?
5389+ /// Time unit for the field to be filtered.
5390+ ///
5391+ /// time unit for the field to be filtered.
5392+ let timeUnit: TimeUnit?
5393+ /// An array of inclusive minimum and maximum values
5394+ /// for a field value of a data item to be included in the filtered data.
5395+ let range: [RangeElement]?
5396+ /// A set of values that the `field`'s value should be a member of,
5397+ /// for a data item included in the filtered data.
5398+ let oneOf: [Equal]?
5399+ /// Filter using a selection name.
5400+ let selection: SelectionOperand?
5401+
5402+ enum CodingKeys: String, CodingKey {
5403+ case not = "not"
5404+ case and = "and"
5405+ case or = "or"
5406+ case equal = "equal"
5407+ case field = "field"
5408+ case timeUnit = "timeUnit"
5409+ case range = "range"
5410+ case oneOf = "oneOf"
5411+ case selection = "selection"
5412+ }
5413+}
5414+
5415+// MARK: Predicate convenience initializers and mutators
5416+
5417+extension Predicate {
5418+ init(data: Data) throws {
5419+ self = try newJSONDecoder().decode(Predicate.self, from: data)
5420+ }
5421+
5422+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5423+ guard let data = json.data(using: encoding) else {
5424+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5425+ }
5426+ try self.init(data: data)
5427+ }
5428+
5429+ init(fromURL url: URL) throws {
5430+ try self.init(data: try Data(contentsOf: url))
5431+ }
5432+
5433+ func with(
5434+ not: LogicalOperandPredicate?? = nil,
5435+ and: [LogicalOperandPredicate]?? = nil,
5436+ or: [LogicalOperandPredicate]?? = nil,
5437+ equal: Equal?? = nil,
5438+ field: String?? = nil,
5439+ timeUnit: TimeUnit?? = nil,
5440+ range: [RangeElement]?? = nil,
5441+ oneOf: [Equal]?? = nil,
5442+ selection: SelectionOperand?? = nil
5443+ ) -> Predicate {
5444+ return Predicate(
5445+ not: not ?? self.not,
5446+ and: and ?? self.and,
5447+ or: or ?? self.or,
5448+ equal: equal ?? self.equal,
5449+ field: field ?? self.field,
5450+ timeUnit: timeUnit ?? self.timeUnit,
5451+ range: range ?? self.range,
5452+ oneOf: oneOf ?? self.oneOf,
5453+ selection: selection ?? self.selection
5454+ )
5455+ }
5456+
5457+ func jsonData() throws -> Data {
5458+ return try newJSONEncoder().encode(self)
5459+ }
5460+
5461+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5462+ return String(data: try self.jsonData(), encoding: encoding)
5463+ }
5464+}
5465+
5466+/// The `filter` property must be one of the predicate definitions:
5467+/// (1) an [expression](types.html#expression) string,
5468+/// where `datum` can be used to refer to the current data object;
5469+/// (2) one of the field predicates: [equal predicate](filter.html#equal-predicate);
5470+/// [range predicate](filter.html#range-predicate), [one-of
5471+/// predicate](filter.html#one-of-predicate);
5472+/// (3) a [selection predicate](filter.html#selection-predicate);
5473+/// or (4) a logical operand that combines (1), (2), or (3).
5474+indirect enum LogicalOperandPredicate: Codable {
5475+ case predicate(Predicate)
5476+ case string(String)
5477+
5478+ init(from decoder: Decoder) throws {
5479+ let container = try decoder.singleValueContainer()
5480+ if let x = try? container.decode(String.self) {
5481+ self = .string(x)
5482+ return
5483+ }
5484+ if let x = try? container.decode(Predicate.self) {
5485+ self = .predicate(x)
5486+ return
5487+ }
5488+ throw DecodingError.typeMismatch(LogicalOperandPredicate.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for LogicalOperandPredicate"))
5489+ }
5490+
5491+ func encode(to encoder: Encoder) throws {
5492+ var container = encoder.singleValueContainer()
5493+ switch self {
5494+ case .predicate(let x):
5495+ try container.encode(x)
5496+ case .string(let x):
5497+ try container.encode(x)
5498+ }
5499+ }
5500+}
5501+
5502+/// The value that the field should be equal to.
5503+enum Equal: Codable {
5504+ case bool(Bool)
5505+ case dateTime(DateTime)
5506+ case double(Double)
5507+ case string(String)
5508+
5509+ init(from decoder: Decoder) throws {
5510+ let container = try decoder.singleValueContainer()
5511+ if let x = try? container.decode(Bool.self) {
5512+ self = .bool(x)
5513+ return
5514+ }
5515+ if let x = try? container.decode(Double.self) {
5516+ self = .double(x)
5517+ return
5518+ }
5519+ if let x = try? container.decode(String.self) {
5520+ self = .string(x)
5521+ return
5522+ }
5523+ if let x = try? container.decode(DateTime.self) {
5524+ self = .dateTime(x)
5525+ return
5526+ }
5527+ throw DecodingError.typeMismatch(Equal.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Equal"))
5528+ }
5529+
5530+ func encode(to encoder: Encoder) throws {
5531+ var container = encoder.singleValueContainer()
5532+ switch self {
5533+ case .bool(let x):
5534+ try container.encode(x)
5535+ case .dateTime(let x):
5536+ try container.encode(x)
5537+ case .double(let x):
5538+ try container.encode(x)
5539+ case .string(let x):
5540+ try container.encode(x)
5541+ }
5542+ }
5543+}
5544+
5545+/// Object for defining datetime in Vega-Lite Filter.
5546+/// If both month and quarter are provided, month has higher precedence.
5547+/// `day` cannot be combined with other date.
5548+/// We accept string for month and day names.
5549+// MARK: - DateTime
5550+struct DateTime: Codable {
5551+ /// Integer value representing the date from 1-31.
5552+ let date: Double?
5553+ /// Value representing the day of a week. This can be one of: (1) integer value -- `1`
5554+ /// represents Monday; (2) case-insensitive day name (e.g., `"Monday"`); (3)
5555+ /// case-insensitive, 3-character short day name (e.g., `"Mon"`). <br/> **Warning:** A
5556+ /// DateTime definition object with `day`** should not be combined with `year`, `quarter`,
5557+ /// `month`, or `date`.
5558+ let day: Day?
5559+ /// Integer value representing the hour of a day from 0-23.
5560+ let hours: Double?
5561+ /// Integer value representing the millisecond segment of time.
5562+ let milliseconds: Double?
5563+ /// Integer value representing the minute segment of time from 0-59.
5564+ let minutes: Double?
5565+ /// One of: (1) integer value representing the month from `1`-`12`. `1` represents January;
5566+ /// (2) case-insensitive month name (e.g., `"January"`); (3) case-insensitive, 3-character
5567+ /// short month name (e.g., `"Jan"`).
5568+ let month: Month?
5569+ /// Integer value representing the quarter of the year (from 1-4).
5570+ let quarter: Double?
5571+ /// Integer value representing the second segment (0-59) of a time value
5572+ let seconds: Double?
5573+ /// A boolean flag indicating if date time is in utc time. If false, the date time is in
5574+ /// local time
5575+ let utc: Bool?
5576+ /// Integer value representing the year.
5577+ let year: Double?
5578+
5579+ enum CodingKeys: String, CodingKey {
5580+ case date = "date"
5581+ case day = "day"
5582+ case hours = "hours"
5583+ case milliseconds = "milliseconds"
5584+ case minutes = "minutes"
5585+ case month = "month"
5586+ case quarter = "quarter"
5587+ case seconds = "seconds"
5588+ case utc = "utc"
5589+ case year = "year"
5590+ }
5591+}
5592+
5593+// MARK: DateTime convenience initializers and mutators
5594+
5595+extension DateTime {
5596+ init(data: Data) throws {
5597+ self = try newJSONDecoder().decode(DateTime.self, from: data)
5598+ }
5599+
5600+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5601+ guard let data = json.data(using: encoding) else {
5602+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5603+ }
5604+ try self.init(data: data)
5605+ }
5606+
5607+ init(fromURL url: URL) throws {
5608+ try self.init(data: try Data(contentsOf: url))
5609+ }
5610+
5611+ func with(
5612+ date: Double?? = nil,
5613+ day: Day?? = nil,
5614+ hours: Double?? = nil,
5615+ milliseconds: Double?? = nil,
5616+ minutes: Double?? = nil,
5617+ month: Month?? = nil,
5618+ quarter: Double?? = nil,
5619+ seconds: Double?? = nil,
5620+ utc: Bool?? = nil,
5621+ year: Double?? = nil
5622+ ) -> DateTime {
5623+ return DateTime(
5624+ date: date ?? self.date,
5625+ day: day ?? self.day,
5626+ hours: hours ?? self.hours,
5627+ milliseconds: milliseconds ?? self.milliseconds,
5628+ minutes: minutes ?? self.minutes,
5629+ month: month ?? self.month,
5630+ quarter: quarter ?? self.quarter,
5631+ seconds: seconds ?? self.seconds,
5632+ utc: utc ?? self.utc,
5633+ year: year ?? self.year
5634+ )
5635+ }
5636+
5637+ func jsonData() throws -> Data {
5638+ return try newJSONEncoder().encode(self)
5639+ }
5640+
5641+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5642+ return String(data: try self.jsonData(), encoding: encoding)
5643+ }
5644+}
5645+
5646+/// Value representing the day of a week. This can be one of: (1) integer value -- `1`
5647+/// represents Monday; (2) case-insensitive day name (e.g., `"Monday"`); (3)
5648+/// case-insensitive, 3-character short day name (e.g., `"Mon"`). <br/> **Warning:** A
5649+/// DateTime definition object with `day`** should not be combined with `year`, `quarter`,
5650+/// `month`, or `date`.
5651+enum Day: Codable {
5652+ case double(Double)
5653+ case string(String)
5654+
5655+ init(from decoder: Decoder) throws {
5656+ let container = try decoder.singleValueContainer()
5657+ if let x = try? container.decode(Double.self) {
5658+ self = .double(x)
5659+ return
5660+ }
5661+ if let x = try? container.decode(String.self) {
5662+ self = .string(x)
5663+ return
5664+ }
5665+ throw DecodingError.typeMismatch(Day.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Day"))
5666+ }
5667+
5668+ func encode(to encoder: Encoder) throws {
5669+ var container = encoder.singleValueContainer()
5670+ switch self {
5671+ case .double(let x):
5672+ try container.encode(x)
5673+ case .string(let x):
5674+ try container.encode(x)
5675+ }
5676+ }
5677+}
5678+
5679+/// One of: (1) integer value representing the month from `1`-`12`. `1` represents January;
5680+/// (2) case-insensitive month name (e.g., `"January"`); (3) case-insensitive, 3-character
5681+/// short month name (e.g., `"Jan"`).
5682+enum Month: Codable {
5683+ case double(Double)
5684+ case string(String)
5685+
5686+ init(from decoder: Decoder) throws {
5687+ let container = try decoder.singleValueContainer()
5688+ if let x = try? container.decode(Double.self) {
5689+ self = .double(x)
5690+ return
5691+ }
5692+ if let x = try? container.decode(String.self) {
5693+ self = .string(x)
5694+ return
5695+ }
5696+ throw DecodingError.typeMismatch(Month.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Month"))
5697+ }
5698+
5699+ func encode(to encoder: Encoder) throws {
5700+ var container = encoder.singleValueContainer()
5701+ switch self {
5702+ case .double(let x):
5703+ try container.encode(x)
5704+ case .string(let x):
5705+ try container.encode(x)
5706+ }
5707+ }
5708+}
5709+
5710+enum RangeElement: Codable {
5711+ case dateTime(DateTime)
5712+ case double(Double)
5713+ case null
5714+
5715+ init(from decoder: Decoder) throws {
5716+ let container = try decoder.singleValueContainer()
5717+ if let x = try? container.decode(Double.self) {
5718+ self = .double(x)
5719+ return
5720+ }
5721+ if let x = try? container.decode(DateTime.self) {
5722+ self = .dateTime(x)
5723+ return
5724+ }
5725+ if container.decodeNil() {
5726+ self = .null
5727+ return
5728+ }
5729+ throw DecodingError.typeMismatch(RangeElement.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for RangeElement"))
5730+ }
5731+
5732+ func encode(to encoder: Encoder) throws {
5733+ var container = encoder.singleValueContainer()
5734+ switch self {
5735+ case .dateTime(let x):
5736+ try container.encode(x)
5737+ case .double(let x):
5738+ try container.encode(x)
5739+ case .null:
5740+ try container.encodeNil()
5741+ }
5742+ }
5743+}
5744+
5745+/// Time unit for the field to be filtered.
5746+///
5747+/// time unit for the field to be filtered.
5748+///
5749+/// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
5750+/// or [a temporal field that gets casted as ordinal](type.html#cast).
5751+///
5752+/// __Default value:__ `undefined` (None)
5753+///
5754+/// The timeUnit.
5755+enum TimeUnit: String, Codable {
5756+ case year = "year"
5757+ case quarter = "quarter"
5758+ case month = "month"
5759+ case day = "day"
5760+ case date = "date"
5761+ case hours = "hours"
5762+ case minutes = "minutes"
5763+ case seconds = "seconds"
5764+ case milliseconds = "milliseconds"
5765+ case utcyear = "utcyear"
5766+ case utcquarter = "utcquarter"
5767+ case utcmonth = "utcmonth"
5768+ case utcday = "utcday"
5769+ case utcdate = "utcdate"
5770+ case utchours = "utchours"
5771+ case utcminutes = "utcminutes"
5772+ case utcseconds = "utcseconds"
5773+ case utcmilliseconds = "utcmilliseconds"
5774+ case yearquarter = "yearquarter"
5775+ case yearquartermonth = "yearquartermonth"
5776+ case yearmonth = "yearmonth"
5777+ case yearmonthdate = "yearmonthdate"
5778+ case yearmonthdatehours = "yearmonthdatehours"
5779+ case yearmonthdatehoursminutes = "yearmonthdatehoursminutes"
5780+ case yearmonthdatehoursminutesseconds = "yearmonthdatehoursminutesseconds"
5781+ case quartermonth = "quartermonth"
5782+ case monthdate = "monthdate"
5783+ case hoursminutes = "hoursminutes"
5784+ case hoursminutesseconds = "hoursminutesseconds"
5785+ case minutesseconds = "minutesseconds"
5786+ case secondsmilliseconds = "secondsmilliseconds"
5787+ case utcyearquarter = "utcyearquarter"
5788+ case utcyearquartermonth = "utcyearquartermonth"
5789+ case utcyearmonth = "utcyearmonth"
5790+ case utcyearmonthdate = "utcyearmonthdate"
5791+ case utcyearmonthdatehours = "utcyearmonthdatehours"
5792+ case utcyearmonthdatehoursminutes = "utcyearmonthdatehoursminutes"
5793+ case utcyearmonthdatehoursminutesseconds = "utcyearmonthdatehoursminutesseconds"
5794+ case utcquartermonth = "utcquartermonth"
5795+ case utcmonthdate = "utcmonthdate"
5796+ case utchoursminutes = "utchoursminutes"
5797+ case utchoursminutesseconds = "utchoursminutesseconds"
5798+ case utcminutesseconds = "utcminutesseconds"
5799+ case utcsecondsmilliseconds = "utcsecondsmilliseconds"
5800+}
5801+
5802+/// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
5803+/// `0` to `1` for opacity).
5804+///
5805+/// A constant value in visual domain.
5806+enum ConditionalValueDefValue: Codable {
5807+ case bool(Bool)
5808+ case double(Double)
5809+ case string(String)
5810+
5811+ init(from decoder: Decoder) throws {
5812+ let container = try decoder.singleValueContainer()
5813+ if let x = try? container.decode(Bool.self) {
5814+ self = .bool(x)
5815+ return
5816+ }
5817+ if let x = try? container.decode(Double.self) {
5818+ self = .double(x)
5819+ return
5820+ }
5821+ if let x = try? container.decode(String.self) {
5822+ self = .string(x)
5823+ return
5824+ }
5825+ throw DecodingError.typeMismatch(ConditionalValueDefValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ConditionalValueDefValue"))
5826+ }
5827+
5828+ func encode(to encoder: Encoder) throws {
5829+ var container = encoder.singleValueContainer()
5830+ switch self {
5831+ case .bool(let x):
5832+ try container.encode(x)
5833+ case .double(let x):
5834+ try container.encode(x)
5835+ case .string(let x):
5836+ try container.encode(x)
5837+ }
5838+ }
5839+}
5840+
5841+// MARK: - ConditionalPredicateMarkPropFieldDefClass
5842+struct ConditionalPredicateMarkPropFieldDefClass: Codable {
5843+ let test: LogicalOperandPredicate?
5844+ /// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
5845+ /// `0` to `1` for opacity).
5846+ let value: ConditionalValueDefValue?
5847+ /// A [selection name](selection.html), or a series of [composed
5848+ /// selections](selection.html#compose).
5849+ let selection: SelectionOperand?
5850+ /// Aggregation function for the field
5851+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
5852+ ///
5853+ /// __Default value:__ `undefined` (None)
5854+ let aggregate: AggregateOp?
5855+ /// A flag for binning a `quantitative` field, or [an object defining binning
5856+ /// parameters](bin.html#params).
5857+ /// If `true`, default [binning parameters](bin.html) will be applied.
5858+ ///
5859+ /// __Default value:__ `false`
5860+ let bin: Bin?
5861+ /// __Required.__ A string defining the name of the field from which to pull a data value
5862+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
5863+ ///
5864+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
5865+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
5866+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
5867+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
5868+ /// See more details about escaping in the [field documentation](field.html).
5869+ ///
5870+ /// __Note:__ `field` is not required if `aggregate` is `count`.
5871+ let field: Field?
5872+ /// An object defining properties of the legend.
5873+ /// If `null`, the legend for the encoding channel will be removed.
5874+ ///
5875+ /// __Default value:__ If undefined, default [legend properties](legend.html) are applied.
5876+ let legend: Legend?
5877+ /// An object defining properties of the channel's scale, which is the function that
5878+ /// transforms values in the data domain (numbers, dates, strings, etc) to visual values
5879+ /// (pixels, colors, sizes) of the encoding channels.
5880+ ///
5881+ /// __Default value:__ If undefined, default [scale properties](scale.html) are applied.
5882+ let scale: Scale?
5883+ /// Sort order for the encoded field.
5884+ /// Supported `sort` values include `"ascending"`, `"descending"` and `null` (no sorting).
5885+ /// For fields with discrete domains, `sort` can also be a [sort field definition
5886+ /// object](sort.html#sort-field).
5887+ ///
5888+ /// __Default value:__ `"ascending"`
5889+ let sort: SortUnion?
5890+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
5891+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
5892+ ///
5893+ /// __Default value:__ `undefined` (None)
5894+ let timeUnit: TimeUnit?
5895+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
5896+ /// `"nominal"`).
5897+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
5898+ /// [geographic projection](projection.html) is applied.
5899+ let type: ConditionalPredicateValueDefType?
5900+
5901+ enum CodingKeys: String, CodingKey {
5902+ case test = "test"
5903+ case value = "value"
5904+ case selection = "selection"
5905+ case aggregate = "aggregate"
5906+ case bin = "bin"
5907+ case field = "field"
5908+ case legend = "legend"
5909+ case scale = "scale"
5910+ case sort = "sort"
5911+ case timeUnit = "timeUnit"
5912+ case type = "type"
5913+ }
5914+}
5915+
5916+// MARK: ConditionalPredicateMarkPropFieldDefClass convenience initializers and mutators
5917+
5918+extension ConditionalPredicateMarkPropFieldDefClass {
5919+ init(data: Data) throws {
5920+ self = try newJSONDecoder().decode(ConditionalPredicateMarkPropFieldDefClass.self, from: data)
5921+ }
5922+
5923+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
5924+ guard let data = json.data(using: encoding) else {
5925+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
5926+ }
5927+ try self.init(data: data)
5928+ }
5929+
5930+ init(fromURL url: URL) throws {
5931+ try self.init(data: try Data(contentsOf: url))
5932+ }
5933+
5934+ func with(
5935+ test: LogicalOperandPredicate?? = nil,
5936+ value: ConditionalValueDefValue?? = nil,
5937+ selection: SelectionOperand?? = nil,
5938+ aggregate: AggregateOp?? = nil,
5939+ bin: Bin?? = nil,
5940+ field: Field?? = nil,
5941+ legend: Legend?? = nil,
5942+ scale: Scale?? = nil,
5943+ sort: SortUnion?? = nil,
5944+ timeUnit: TimeUnit?? = nil,
5945+ type: ConditionalPredicateValueDefType?? = nil
5946+ ) -> ConditionalPredicateMarkPropFieldDefClass {
5947+ return ConditionalPredicateMarkPropFieldDefClass(
5948+ test: test ?? self.test,
5949+ value: value ?? self.value,
5950+ selection: selection ?? self.selection,
5951+ aggregate: aggregate ?? self.aggregate,
5952+ bin: bin ?? self.bin,
5953+ field: field ?? self.field,
5954+ legend: legend ?? self.legend,
5955+ scale: scale ?? self.scale,
5956+ sort: sort ?? self.sort,
5957+ timeUnit: timeUnit ?? self.timeUnit,
5958+ type: type ?? self.type
5959+ )
5960+ }
5961+
5962+ func jsonData() throws -> Data {
5963+ return try newJSONEncoder().encode(self)
5964+ }
5965+
5966+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
5967+ return String(data: try self.jsonData(), encoding: encoding)
5968+ }
5969+}
5970+
5971+enum Field: Codable {
5972+ case repeatRef(RepeatRef)
5973+ case string(String)
5974+
5975+ init(from decoder: Decoder) throws {
5976+ let container = try decoder.singleValueContainer()
5977+ if let x = try? container.decode(String.self) {
5978+ self = .string(x)
5979+ return
5980+ }
5981+ if let x = try? container.decode(RepeatRef.self) {
5982+ self = .repeatRef(x)
5983+ return
5984+ }
5985+ throw DecodingError.typeMismatch(Field.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Field"))
5986+ }
5987+
5988+ func encode(to encoder: Encoder) throws {
5989+ var container = encoder.singleValueContainer()
5990+ switch self {
5991+ case .repeatRef(let x):
5992+ try container.encode(x)
5993+ case .string(let x):
5994+ try container.encode(x)
5995+ }
5996+ }
5997+}
5998+
5999+/// Reference to a repeated value.
6000+// MARK: - RepeatRef
6001+struct RepeatRef: Codable {
6002+ let repeatRefRepeat: RepeatEnum
6003+
6004+ enum CodingKeys: String, CodingKey {
6005+ case repeatRefRepeat = "repeat"
6006+ }
6007+}
6008+
6009+// MARK: RepeatRef convenience initializers and mutators
6010+
6011+extension RepeatRef {
6012+ init(data: Data) throws {
6013+ self = try newJSONDecoder().decode(RepeatRef.self, from: data)
6014+ }
6015+
6016+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6017+ guard let data = json.data(using: encoding) else {
6018+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6019+ }
6020+ try self.init(data: data)
6021+ }
6022+
6023+ init(fromURL url: URL) throws {
6024+ try self.init(data: try Data(contentsOf: url))
6025+ }
6026+
6027+ func with(
6028+ repeatRefRepeat: RepeatEnum? = nil
6029+ ) -> RepeatRef {
6030+ return RepeatRef(
6031+ repeatRefRepeat: repeatRefRepeat ?? self.repeatRefRepeat
6032+ )
6033+ }
6034+
6035+ func jsonData() throws -> Data {
6036+ return try newJSONEncoder().encode(self)
6037+ }
6038+
6039+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6040+ return String(data: try self.jsonData(), encoding: encoding)
6041+ }
6042+}
6043+
6044+enum RepeatEnum: String, Codable {
6045+ case row = "row"
6046+ case column = "column"
6047+}
6048+
6049+/// Properties of a legend or boolean flag for determining whether to show it.
6050+// MARK: - Legend
6051+struct Legend: Codable {
6052+ /// Padding (in pixels) between legend entries in a symbol legend.
6053+ let entryPadding: Double?
6054+ /// The formatting pattern for labels. This is D3's [number format
6055+ /// pattern](https://github.com/d3/d3-format#locale_format) for quantitative fields and D3's
6056+ /// [time format pattern](https://github.com/d3/d3-time-format#locale_format) for time
6057+ /// field.
6058+ ///
6059+ /// See the [format documentation](format.html) for more information.
6060+ ///
6061+ /// __Default value:__ derived from [numberFormat](config.html#format) config for
6062+ /// quantitative fields and from [timeFormat](config.html#format) config for temporal fields.
6063+ let format: String?
6064+ /// The offset, in pixels, by which to displace the legend from the edge of the enclosing
6065+ /// group or data rectangle.
6066+ ///
6067+ /// __Default value:__ `0`
6068+ let offset: Double?
6069+ /// The orientation of the legend, which determines how the legend is positioned within the
6070+ /// scene. One of "left", "right", "top-left", "top-right", "bottom-left", "bottom-right",
6071+ /// "none".
6072+ ///
6073+ /// __Default value:__ `"right"`
6074+ let orient: LegendOrient?
6075+ /// The padding, in pixels, between the legend and axis.
6076+ let padding: Double?
6077+ /// The desired number of tick values for quantitative legends.
6078+ let tickCount: Double?
6079+ /// A title for the field. If `null`, the title will be removed.
6080+ ///
6081+ /// __Default value:__ derived from the field's name and transformation function
6082+ /// (`aggregate`, `bin` and `timeUnit`). If the field has an aggregate function, the
6083+ /// function is displayed as a part of the title (e.g., `"Sum of Profit"`). If the field is
6084+ /// binned or has a time unit applied, the applied function will be denoted in parentheses
6085+ /// (e.g., `"Profit (binned)"`, `"Transaction Date (year-month)"`). Otherwise, the title is
6086+ /// simply the field name.
6087+ ///
6088+ /// __Note__: You can customize the default field title format by providing the [`fieldTitle`
6089+ /// property in the [config](config.html) or [`fieldTitle` function via the `compile`
6090+ /// function's options](compile.html#field-title).
6091+ let title: String?
6092+ /// The type of the legend. Use `"symbol"` to create a discrete legend and `"gradient"` for a
6093+ /// continuous color gradient.
6094+ ///
6095+ /// __Default value:__ `"gradient"` for non-binned quantitative fields and temporal fields;
6096+ /// `"symbol"` otherwise.
6097+ let type: LegendType?
6098+ /// Explicitly set the visible legend values.
6099+ let values: [LegendValue]?
6100+ /// A non-positive integer indicating z-index of the legend.
6101+ /// If zindex is 0, legend should be drawn behind all chart elements.
6102+ /// To put them in front, use zindex = 1.
6103+ let zindex: Double?
6104+
6105+ enum CodingKeys: String, CodingKey {
6106+ case entryPadding = "entryPadding"
6107+ case format = "format"
6108+ case offset = "offset"
6109+ case orient = "orient"
6110+ case padding = "padding"
6111+ case tickCount = "tickCount"
6112+ case title = "title"
6113+ case type = "type"
6114+ case values = "values"
6115+ case zindex = "zindex"
6116+ }
6117+}
6118+
6119+// MARK: Legend convenience initializers and mutators
6120+
6121+extension Legend {
6122+ init(data: Data) throws {
6123+ self = try newJSONDecoder().decode(Legend.self, from: data)
6124+ }
6125+
6126+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6127+ guard let data = json.data(using: encoding) else {
6128+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6129+ }
6130+ try self.init(data: data)
6131+ }
6132+
6133+ init(fromURL url: URL) throws {
6134+ try self.init(data: try Data(contentsOf: url))
6135+ }
6136+
6137+ func with(
6138+ entryPadding: Double?? = nil,
6139+ format: String?? = nil,
6140+ offset: Double?? = nil,
6141+ orient: LegendOrient?? = nil,
6142+ padding: Double?? = nil,
6143+ tickCount: Double?? = nil,
6144+ title: String?? = nil,
6145+ type: LegendType?? = nil,
6146+ values: [LegendValue]?? = nil,
6147+ zindex: Double?? = nil
6148+ ) -> Legend {
6149+ return Legend(
6150+ entryPadding: entryPadding ?? self.entryPadding,
6151+ format: format ?? self.format,
6152+ offset: offset ?? self.offset,
6153+ orient: orient ?? self.orient,
6154+ padding: padding ?? self.padding,
6155+ tickCount: tickCount ?? self.tickCount,
6156+ title: title ?? self.title,
6157+ type: type ?? self.type,
6158+ values: values ?? self.values,
6159+ zindex: zindex ?? self.zindex
6160+ )
6161+ }
6162+
6163+ func jsonData() throws -> Data {
6164+ return try newJSONEncoder().encode(self)
6165+ }
6166+
6167+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6168+ return String(data: try self.jsonData(), encoding: encoding)
6169+ }
6170+}
6171+
6172+/// The type of the legend. Use `"symbol"` to create a discrete legend and `"gradient"` for a
6173+/// continuous color gradient.
6174+///
6175+/// __Default value:__ `"gradient"` for non-binned quantitative fields and temporal fields;
6176+/// `"symbol"` otherwise.
6177+enum LegendType: String, Codable {
6178+ case symbol = "symbol"
6179+ case gradient = "gradient"
6180+}
6181+
6182+enum LegendValue: Codable {
6183+ case dateTime(DateTime)
6184+ case double(Double)
6185+ case string(String)
6186+
6187+ init(from decoder: Decoder) throws {
6188+ let container = try decoder.singleValueContainer()
6189+ if let x = try? container.decode(Double.self) {
6190+ self = .double(x)
6191+ return
6192+ }
6193+ if let x = try? container.decode(String.self) {
6194+ self = .string(x)
6195+ return
6196+ }
6197+ if let x = try? container.decode(DateTime.self) {
6198+ self = .dateTime(x)
6199+ return
6200+ }
6201+ throw DecodingError.typeMismatch(LegendValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for LegendValue"))
6202+ }
6203+
6204+ func encode(to encoder: Encoder) throws {
6205+ var container = encoder.singleValueContainer()
6206+ switch self {
6207+ case .dateTime(let x):
6208+ try container.encode(x)
6209+ case .double(let x):
6210+ try container.encode(x)
6211+ case .string(let x):
6212+ try container.encode(x)
6213+ }
6214+ }
6215+}
6216+
6217+/// An object defining properties of the channel's scale, which is the function that
6218+/// transforms values in the data domain (numbers, dates, strings, etc) to visual values
6219+/// (pixels, colors, sizes) of the encoding channels.
6220+///
6221+/// __Default value:__ If undefined, default [scale properties](scale.html) are applied.
6222+// MARK: - Scale
6223+struct Scale: Codable {
6224+ /// The logarithm base of the `log` scale (default `10`).
6225+ let base: Double?
6226+ /// If `true`, values that exceed the data domain are clamped to either the minimum or
6227+ /// maximum range value
6228+ ///
6229+ /// __Default value:__ derived from the [scale config](config.html#scale-config)'s `clamp`
6230+ /// (`true` by default).
6231+ let clamp: Bool?
6232+ /// Customized domain values.
6233+ ///
6234+ /// For _quantitative_ fields, `domain` can take the form of a two-element array with minimum
6235+ /// and maximum values. [Piecewise scales](scale.html#piecewise) can be created by providing
6236+ /// a `domain` with more than two entries.
6237+ /// If the input field is aggregated, `domain` can also be a string value `"unaggregated"`,
6238+ /// indicating that the domain should include the raw data values prior to the aggregation.
6239+ ///
6240+ /// For _temporal_ fields, `domain` can be a two-element array minimum and maximum values, in
6241+ /// the form of either timestamps or the [DateTime definition objects](types.html#datetime).
6242+ ///
6243+ /// For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input
6244+ /// values.
6245+ ///
6246+ /// The `selection` property can be used to [interactively
6247+ /// determine](selection.html#scale-domains) the scale domain.
6248+ let domain: DomainUnion?
6249+ /// The exponent of the `pow` scale.
6250+ let exponent: Double?
6251+ /// The interpolation method for range values. By default, a general interpolator for
6252+ /// numbers, dates, strings and colors (in RGB space) is used. For color ranges, this
6253+ /// property allows interpolation in alternative color spaces. Legal values include `rgb`,
6254+ /// `hsl`, `hsl-long`, `lab`, `hcl`, `hcl-long`, `cubehelix` and `cubehelix-long` ('-long'
6255+ /// variants use longer paths in polar coordinate spaces). If object-valued, this property
6256+ /// accepts an object with a string-valued _type_ property and an optional numeric _gamma_
6257+ /// property applicable to rgb and cubehelix interpolators. For more, see the [d3-interpolate
6258+ /// documentation](https://github.com/d3/d3-interpolate).
6259+ ///
6260+ /// __Note:__ Sequential scales do not support `interpolate` as they have a fixed
6261+ /// interpolator. Since Vega-Lite uses sequential scales for quantitative fields by default,
6262+ /// you have to set the scale `type` to other quantitative scale type such as `"linear"` to
6263+ /// customize `interpolate`.
6264+ let interpolate: InterpolateUnion?
6265+ /// Extending the domain so that it starts and ends on nice round values. This method
6266+ /// typically modifies the scale’s domain, and may only extend the bounds to the nearest
6267+ /// round value. Nicing is useful if the domain is computed from data and may be irregular.
6268+ /// For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2,
6269+ /// 1.0]_.
6270+ ///
6271+ /// For quantitative scales such as linear, `nice` can be either a boolean flag or a number.
6272+ /// If `nice` is a number, it will represent a desired tick count. This allows greater
6273+ /// control over the step size used to extend the bounds, guaranteeing that the returned
6274+ /// ticks will exactly cover the domain.
6275+ ///
6276+ /// For temporal fields with time and utc scales, the `nice` value can be a string indicating
6277+ /// the desired time interval. Legal values are `"millisecond"`, `"second"`, `"minute"`,
6278+ /// `"hour"`, `"day"`, `"week"`, `"month"`, and `"year"`. Alternatively, `time` and `utc`
6279+ /// scales can accept an object-valued interval specifier of the form `{"interval": "month",
6280+ /// "step": 3}`, which includes a desired number of interval steps. Here, the domain would
6281+ /// snap to quarter (Jan, Apr, Jul, Oct) boundaries.
6282+ ///
6283+ /// __Default value:__ `true` for unbinned _quantitative_ fields; `false` otherwise.
6284+ let nice: NiceUnion?
6285+ /// For _[continuous](scale.html#continuous)_ scales, expands the scale domain to accommodate
6286+ /// the specified number of pixels on each of the scale range. The scale range must represent
6287+ /// pixels for this parameter to function as intended. Padding adjustment is performed prior
6288+ /// to all other adjustments, including the effects of the zero, nice, domainMin, and
6289+ /// domainMax properties.
6290+ ///
6291+ /// For _[band](scale.html#band)_ scales, shortcut for setting `paddingInner` and
6292+ /// `paddingOuter` to the same value.
6293+ ///
6294+ /// For _[point](scale.html#point)_ scales, alias for `paddingOuter`.
6295+ ///
6296+ /// __Default value:__ For _continuous_ scales, derived from the [scale
6297+ /// config](scale.html#config)'s `continuousPadding`.
6298+ /// For _band and point_ scales, see `paddingInner` and `paddingOuter`.
6299+ let padding: Double?
6300+ /// The inner padding (spacing) within each band step of band scales, as a fraction of the
6301+ /// step size. This value must lie in the range [0,1].
6302+ ///
6303+ /// For point scale, this property is invalid as point scales do not have internal band
6304+ /// widths (only step sizes between bands).
6305+ ///
6306+ /// __Default value:__ derived from the [scale config](scale.html#config)'s
6307+ /// `bandPaddingInner`.
6308+ let paddingInner: Double?
6309+ /// The outer padding (spacing) at the ends of the range of band and point scales,
6310+ /// as a fraction of the step size. This value must lie in the range [0,1].
6311+ ///
6312+ /// __Default value:__ derived from the [scale config](scale.html#config)'s
6313+ /// `bandPaddingOuter` for band scales and `pointPadding` for point scales.
6314+ let paddingOuter: Double?
6315+ /// The range of the scale. One of:
6316+ ///
6317+ /// - A string indicating a [pre-defined named scale range](scale.html#range-config) (e.g.,
6318+ /// example, `"symbol"`, or `"diverging"`).
6319+ ///
6320+ /// - For [continuous scales](scale.html#continuous), two-element array indicating minimum
6321+ /// and maximum values, or an array with more than two entries for specifying a [piecewise
6322+ /// scale](scale.html#piecewise).
6323+ ///
6324+ /// - For [discrete](scale.html#discrete) and [discretizing](scale.html#discretizing) scales,
6325+ /// an array of desired output values.
6326+ ///
6327+ /// __Notes:__
6328+ ///
6329+ /// 1) For [sequential](scale.html#sequential), [ordinal](scale.html#ordinal), and
6330+ /// discretizing color scales, you can also specify a color [`scheme`](scale.html#scheme)
6331+ /// instead of `range`.
6332+ ///
6333+ /// 2) Any directly specified `range` for `x` and `y` channels will be ignored. Range can be
6334+ /// customized via the view's corresponding [size](size.html) (`width` and `height`) or via
6335+ /// [range steps and paddings properties](#range-step) for [band](#band) and [point](#point)
6336+ /// scales.
6337+ let range: ScaleRange?
6338+ /// The distance between the starts of adjacent bands or points in [band](scale.html#band)
6339+ /// and [point](scale.html#point) scales.
6340+ ///
6341+ /// If `rangeStep` is `null` or if the view contains the scale's corresponding
6342+ /// [size](size.html) (`width` for `x` scales and `height` for `y` scales), `rangeStep` will
6343+ /// be automatically determined to fit the size of the view.
6344+ ///
6345+ /// __Default value:__ derived the [scale config](config.html#scale-config)'s
6346+ /// `textXRangeStep` (`90` by default) for x-scales of `text` marks and `rangeStep` (`21` by
6347+ /// default) for x-scales of other marks and y-scales.
6348+ ///
6349+ /// __Warning__: If `rangeStep` is `null` and the cardinality of the scale's domain is higher
6350+ /// than `width` or `height`, the rangeStep might become less than one pixel and the mark
6351+ /// might not appear correctly.
6352+ let rangeStep: Double?
6353+ /// If `true`, rounds numeric output values to integers. This can be helpful for snapping to
6354+ /// the pixel grid.
6355+ ///
6356+ /// __Default value:__ `false`.
6357+ let round: Bool?
6358+ /// A string indicating a color [scheme](scale.html#scheme) name (e.g., `"category10"` or
6359+ /// `"viridis"`) or a [scheme parameter object](scale.html#scheme-params).
6360+ ///
6361+ /// Discrete color schemes may be used with [discrete](scale.html#discrete) or
6362+ /// [discretizing](scale.html#discretizing) scales. Continuous color schemes are intended for
6363+ /// use with [sequential](scales.html#sequential) scales.
6364+ ///
6365+ /// For the full list of supported scheme, please refer to the [Vega
6366+ /// Scheme](https://vega.github.io/vega/docs/schemes/#reference) reference.
6367+ let scheme: Scheme?
6368+ /// The type of scale. Vega-Lite supports the following categories of scale types:
6369+ ///
6370+ /// 1) [**Continuous Scales**](scale.html#continuous) -- mapping continuous domains to
6371+ /// continuous output ranges ([`"linear"`](scale.html#linear), [`"pow"`](scale.html#pow),
6372+ /// [`"sqrt"`](scale.html#sqrt), [`"log"`](scale.html#log), [`"time"`](scale.html#time),
6373+ /// [`"utc"`](scale.html#utc), [`"sequential"`](scale.html#sequential)).
6374+ ///
6375+ /// 2) [**Discrete Scales**](scale.html#discrete) -- mapping discrete domains to discrete
6376+ /// ([`"ordinal"`](scale.html#ordinal)) or continuous ([`"band"`](scale.html#band) and
6377+ /// [`"point"`](scale.html#point)) output ranges.
6378+ ///
6379+ /// 3) [**Discretizing Scales**](scale.html#discretizing) -- mapping continuous domains to
6380+ /// discrete output ranges ([`"bin-linear"`](scale.html#bin-linear) and
6381+ /// [`"bin-ordinal"`](scale.html#bin-ordinal)).
6382+ ///
6383+ /// __Default value:__ please see the [scale type table](scale.html#type).
6384+ let type: ScaleType?
6385+ /// If `true`, ensures that a zero baseline value is included in the scale domain.
6386+ ///
6387+ /// __Default value:__ `true` for x and y channels if the quantitative field is not binned
6388+ /// and no custom `domain` is provided; `false` otherwise.
6389+ ///
6390+ /// __Note:__ Log, time, and utc scales do not support `zero`.
6391+ let zero: Bool?
6392+
6393+ enum CodingKeys: String, CodingKey {
6394+ case base = "base"
6395+ case clamp = "clamp"
6396+ case domain = "domain"
6397+ case exponent = "exponent"
6398+ case interpolate = "interpolate"
6399+ case nice = "nice"
6400+ case padding = "padding"
6401+ case paddingInner = "paddingInner"
6402+ case paddingOuter = "paddingOuter"
6403+ case range = "range"
6404+ case rangeStep = "rangeStep"
6405+ case round = "round"
6406+ case scheme = "scheme"
6407+ case type = "type"
6408+ case zero = "zero"
6409+ }
6410+}
6411+
6412+// MARK: Scale convenience initializers and mutators
6413+
6414+extension Scale {
6415+ init(data: Data) throws {
6416+ self = try newJSONDecoder().decode(Scale.self, from: data)
6417+ }
6418+
6419+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6420+ guard let data = json.data(using: encoding) else {
6421+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6422+ }
6423+ try self.init(data: data)
6424+ }
6425+
6426+ init(fromURL url: URL) throws {
6427+ try self.init(data: try Data(contentsOf: url))
6428+ }
6429+
6430+ func with(
6431+ base: Double?? = nil,
6432+ clamp: Bool?? = nil,
6433+ domain: DomainUnion?? = nil,
6434+ exponent: Double?? = nil,
6435+ interpolate: InterpolateUnion?? = nil,
6436+ nice: NiceUnion?? = nil,
6437+ padding: Double?? = nil,
6438+ paddingInner: Double?? = nil,
6439+ paddingOuter: Double?? = nil,
6440+ range: ScaleRange?? = nil,
6441+ rangeStep: Double?? = nil,
6442+ round: Bool?? = nil,
6443+ scheme: Scheme?? = nil,
6444+ type: ScaleType?? = nil,
6445+ zero: Bool?? = nil
6446+ ) -> Scale {
6447+ return Scale(
6448+ base: base ?? self.base,
6449+ clamp: clamp ?? self.clamp,
6450+ domain: domain ?? self.domain,
6451+ exponent: exponent ?? self.exponent,
6452+ interpolate: interpolate ?? self.interpolate,
6453+ nice: nice ?? self.nice,
6454+ padding: padding ?? self.padding,
6455+ paddingInner: paddingInner ?? self.paddingInner,
6456+ paddingOuter: paddingOuter ?? self.paddingOuter,
6457+ range: range ?? self.range,
6458+ rangeStep: rangeStep ?? self.rangeStep,
6459+ round: round ?? self.round,
6460+ scheme: scheme ?? self.scheme,
6461+ type: type ?? self.type,
6462+ zero: zero ?? self.zero
6463+ )
6464+ }
6465+
6466+ func jsonData() throws -> Data {
6467+ return try newJSONEncoder().encode(self)
6468+ }
6469+
6470+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6471+ return String(data: try self.jsonData(), encoding: encoding)
6472+ }
6473+}
6474+
6475+/// Customized domain values.
6476+///
6477+/// For _quantitative_ fields, `domain` can take the form of a two-element array with minimum
6478+/// and maximum values. [Piecewise scales](scale.html#piecewise) can be created by providing
6479+/// a `domain` with more than two entries.
6480+/// If the input field is aggregated, `domain` can also be a string value `"unaggregated"`,
6481+/// indicating that the domain should include the raw data values prior to the aggregation.
6482+///
6483+/// For _temporal_ fields, `domain` can be a two-element array minimum and maximum values, in
6484+/// the form of either timestamps or the [DateTime definition objects](types.html#datetime).
6485+///
6486+/// For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input
6487+/// values.
6488+///
6489+/// The `selection` property can be used to [interactively
6490+/// determine](selection.html#scale-domains) the scale domain.
6491+enum DomainUnion: Codable {
6492+ case domainClass(DomainClass)
6493+ case enumeration(Domain)
6494+ case unionArray([Equal])
6495+
6496+ init(from decoder: Decoder) throws {
6497+ let container = try decoder.singleValueContainer()
6498+ if let x = try? container.decode([Equal].self) {
6499+ self = .unionArray(x)
6500+ return
6501+ }
6502+ if let x = try? container.decode(Domain.self) {
6503+ self = .enumeration(x)
6504+ return
6505+ }
6506+ if let x = try? container.decode(DomainClass.self) {
6507+ self = .domainClass(x)
6508+ return
6509+ }
6510+ throw DecodingError.typeMismatch(DomainUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DomainUnion"))
6511+ }
6512+
6513+ func encode(to encoder: Encoder) throws {
6514+ var container = encoder.singleValueContainer()
6515+ switch self {
6516+ case .domainClass(let x):
6517+ try container.encode(x)
6518+ case .enumeration(let x):
6519+ try container.encode(x)
6520+ case .unionArray(let x):
6521+ try container.encode(x)
6522+ }
6523+ }
6524+}
6525+
6526+// MARK: - DomainClass
6527+struct DomainClass: Codable {
6528+ /// The field name to extract selected values for, when a selection is
6529+ /// [projected](project.html)
6530+ /// over multiple fields or encodings.
6531+ let field: String?
6532+ /// The name of a selection.
6533+ let selection: String
6534+ /// The encoding channel to extract selected values for, when a selection is
6535+ /// [projected](project.html)
6536+ /// over multiple fields or encodings.
6537+ let encoding: String?
6538+
6539+ enum CodingKeys: String, CodingKey {
6540+ case field = "field"
6541+ case selection = "selection"
6542+ case encoding = "encoding"
6543+ }
6544+}
6545+
6546+// MARK: DomainClass convenience initializers and mutators
6547+
6548+extension DomainClass {
6549+ init(data: Data) throws {
6550+ self = try newJSONDecoder().decode(DomainClass.self, from: data)
6551+ }
6552+
6553+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6554+ guard let data = json.data(using: encoding) else {
6555+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6556+ }
6557+ try self.init(data: data)
6558+ }
6559+
6560+ init(fromURL url: URL) throws {
6561+ try self.init(data: try Data(contentsOf: url))
6562+ }
6563+
6564+ func with(
6565+ field: String?? = nil,
6566+ selection: String? = nil,
6567+ encoding: String?? = nil
6568+ ) -> DomainClass {
6569+ return DomainClass(
6570+ field: field ?? self.field,
6571+ selection: selection ?? self.selection,
6572+ encoding: encoding ?? self.encoding
6573+ )
6574+ }
6575+
6576+ func jsonData() throws -> Data {
6577+ return try newJSONEncoder().encode(self)
6578+ }
6579+
6580+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6581+ return String(data: try self.jsonData(), encoding: encoding)
6582+ }
6583+}
6584+
6585+enum Domain: String, Codable {
6586+ case unaggregated = "unaggregated"
6587+}
6588+
6589+/// The interpolation method for range values. By default, a general interpolator for
6590+/// numbers, dates, strings and colors (in RGB space) is used. For color ranges, this
6591+/// property allows interpolation in alternative color spaces. Legal values include `rgb`,
6592+/// `hsl`, `hsl-long`, `lab`, `hcl`, `hcl-long`, `cubehelix` and `cubehelix-long` ('-long'
6593+/// variants use longer paths in polar coordinate spaces). If object-valued, this property
6594+/// accepts an object with a string-valued _type_ property and an optional numeric _gamma_
6595+/// property applicable to rgb and cubehelix interpolators. For more, see the [d3-interpolate
6596+/// documentation](https://github.com/d3/d3-interpolate).
6597+///
6598+/// __Note:__ Sequential scales do not support `interpolate` as they have a fixed
6599+/// interpolator. Since Vega-Lite uses sequential scales for quantitative fields by default,
6600+/// you have to set the scale `type` to other quantitative scale type such as `"linear"` to
6601+/// customize `interpolate`.
6602+enum InterpolateUnion: Codable {
6603+ case enumeration(Interpolate)
6604+ case interpolateParams(InterpolateParams)
6605+
6606+ init(from decoder: Decoder) throws {
6607+ let container = try decoder.singleValueContainer()
6608+ if let x = try? container.decode(Interpolate.self) {
6609+ self = .enumeration(x)
6610+ return
6611+ }
6612+ if let x = try? container.decode(InterpolateParams.self) {
6613+ self = .interpolateParams(x)
6614+ return
6615+ }
6616+ throw DecodingError.typeMismatch(InterpolateUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for InterpolateUnion"))
6617+ }
6618+
6619+ func encode(to encoder: Encoder) throws {
6620+ var container = encoder.singleValueContainer()
6621+ switch self {
6622+ case .enumeration(let x):
6623+ try container.encode(x)
6624+ case .interpolateParams(let x):
6625+ try container.encode(x)
6626+ }
6627+ }
6628+}
6629+
6630+// MARK: - InterpolateParams
6631+struct InterpolateParams: Codable {
6632+ let gamma: Double?
6633+ let type: InterpolateParamsType
6634+
6635+ enum CodingKeys: String, CodingKey {
6636+ case gamma = "gamma"
6637+ case type = "type"
6638+ }
6639+}
6640+
6641+// MARK: InterpolateParams convenience initializers and mutators
6642+
6643+extension InterpolateParams {
6644+ init(data: Data) throws {
6645+ self = try newJSONDecoder().decode(InterpolateParams.self, from: data)
6646+ }
6647+
6648+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6649+ guard let data = json.data(using: encoding) else {
6650+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6651+ }
6652+ try self.init(data: data)
6653+ }
6654+
6655+ init(fromURL url: URL) throws {
6656+ try self.init(data: try Data(contentsOf: url))
6657+ }
6658+
6659+ func with(
6660+ gamma: Double?? = nil,
6661+ type: InterpolateParamsType? = nil
6662+ ) -> InterpolateParams {
6663+ return InterpolateParams(
6664+ gamma: gamma ?? self.gamma,
6665+ type: type ?? self.type
6666+ )
6667+ }
6668+
6669+ func jsonData() throws -> Data {
6670+ return try newJSONEncoder().encode(self)
6671+ }
6672+
6673+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6674+ return String(data: try self.jsonData(), encoding: encoding)
6675+ }
6676+}
6677+
6678+enum InterpolateParamsType: String, Codable {
6679+ case rgb = "rgb"
6680+ case cubehelix = "cubehelix"
6681+ case cubehelixLong = "cubehelix-long"
6682+}
6683+
6684+/// Extending the domain so that it starts and ends on nice round values. This method
6685+/// typically modifies the scale’s domain, and may only extend the bounds to the nearest
6686+/// round value. Nicing is useful if the domain is computed from data and may be irregular.
6687+/// For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2,
6688+/// 1.0]_.
6689+///
6690+/// For quantitative scales such as linear, `nice` can be either a boolean flag or a number.
6691+/// If `nice` is a number, it will represent a desired tick count. This allows greater
6692+/// control over the step size used to extend the bounds, guaranteeing that the returned
6693+/// ticks will exactly cover the domain.
6694+///
6695+/// For temporal fields with time and utc scales, the `nice` value can be a string indicating
6696+/// the desired time interval. Legal values are `"millisecond"`, `"second"`, `"minute"`,
6697+/// `"hour"`, `"day"`, `"week"`, `"month"`, and `"year"`. Alternatively, `time` and `utc`
6698+/// scales can accept an object-valued interval specifier of the form `{"interval": "month",
6699+/// "step": 3}`, which includes a desired number of interval steps. Here, the domain would
6700+/// snap to quarter (Jan, Apr, Jul, Oct) boundaries.
6701+///
6702+/// __Default value:__ `true` for unbinned _quantitative_ fields; `false` otherwise.
6703+enum NiceUnion: Codable {
6704+ case bool(Bool)
6705+ case double(Double)
6706+ case enumeration(NiceTime)
6707+ case niceClass(NiceClass)
6708+
6709+ init(from decoder: Decoder) throws {
6710+ let container = try decoder.singleValueContainer()
6711+ if let x = try? container.decode(Bool.self) {
6712+ self = .bool(x)
6713+ return
6714+ }
6715+ if let x = try? container.decode(Double.self) {
6716+ self = .double(x)
6717+ return
6718+ }
6719+ if let x = try? container.decode(NiceTime.self) {
6720+ self = .enumeration(x)
6721+ return
6722+ }
6723+ if let x = try? container.decode(NiceClass.self) {
6724+ self = .niceClass(x)
6725+ return
6726+ }
6727+ throw DecodingError.typeMismatch(NiceUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for NiceUnion"))
6728+ }
6729+
6730+ func encode(to encoder: Encoder) throws {
6731+ var container = encoder.singleValueContainer()
6732+ switch self {
6733+ case .bool(let x):
6734+ try container.encode(x)
6735+ case .double(let x):
6736+ try container.encode(x)
6737+ case .enumeration(let x):
6738+ try container.encode(x)
6739+ case .niceClass(let x):
6740+ try container.encode(x)
6741+ }
6742+ }
6743+}
6744+
6745+// MARK: - NiceClass
6746+struct NiceClass: Codable {
6747+ let interval: String
6748+ let step: Double
6749+
6750+ enum CodingKeys: String, CodingKey {
6751+ case interval = "interval"
6752+ case step = "step"
6753+ }
6754+}
6755+
6756+// MARK: NiceClass convenience initializers and mutators
6757+
6758+extension NiceClass {
6759+ init(data: Data) throws {
6760+ self = try newJSONDecoder().decode(NiceClass.self, from: data)
6761+ }
6762+
6763+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6764+ guard let data = json.data(using: encoding) else {
6765+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6766+ }
6767+ try self.init(data: data)
6768+ }
6769+
6770+ init(fromURL url: URL) throws {
6771+ try self.init(data: try Data(contentsOf: url))
6772+ }
6773+
6774+ func with(
6775+ interval: String? = nil,
6776+ step: Double? = nil
6777+ ) -> NiceClass {
6778+ return NiceClass(
6779+ interval: interval ?? self.interval,
6780+ step: step ?? self.step
6781+ )
6782+ }
6783+
6784+ func jsonData() throws -> Data {
6785+ return try newJSONEncoder().encode(self)
6786+ }
6787+
6788+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6789+ return String(data: try self.jsonData(), encoding: encoding)
6790+ }
6791+}
6792+
6793+enum NiceTime: String, Codable {
6794+ case second = "second"
6795+ case minute = "minute"
6796+ case hour = "hour"
6797+ case day = "day"
6798+ case week = "week"
6799+ case month = "month"
6800+ case year = "year"
6801+}
6802+
6803+/// The range of the scale. One of:
6804+///
6805+/// - A string indicating a [pre-defined named scale range](scale.html#range-config) (e.g.,
6806+/// example, `"symbol"`, or `"diverging"`).
6807+///
6808+/// - For [continuous scales](scale.html#continuous), two-element array indicating minimum
6809+/// and maximum values, or an array with more than two entries for specifying a [piecewise
6810+/// scale](scale.html#piecewise).
6811+///
6812+/// - For [discrete](scale.html#discrete) and [discretizing](scale.html#discretizing) scales,
6813+/// an array of desired output values.
6814+///
6815+/// __Notes:__
6816+///
6817+/// 1) For [sequential](scale.html#sequential), [ordinal](scale.html#ordinal), and
6818+/// discretizing color scales, you can also specify a color [`scheme`](scale.html#scheme)
6819+/// instead of `range`.
6820+///
6821+/// 2) Any directly specified `range` for `x` and `y` channels will be ignored. Range can be
6822+/// customized via the view's corresponding [size](size.html) (`width` and `height`) or via
6823+/// [range steps and paddings properties](#range-step) for [band](#band) and [point](#point)
6824+/// scales.
6825+enum ScaleRange: Codable {
6826+ case string(String)
6827+ case unionArray([TitleFontWeight])
6828+
6829+ init(from decoder: Decoder) throws {
6830+ let container = try decoder.singleValueContainer()
6831+ if let x = try? container.decode([TitleFontWeight].self) {
6832+ self = .unionArray(x)
6833+ return
6834+ }
6835+ if let x = try? container.decode(String.self) {
6836+ self = .string(x)
6837+ return
6838+ }
6839+ throw DecodingError.typeMismatch(ScaleRange.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ScaleRange"))
6840+ }
6841+
6842+ func encode(to encoder: Encoder) throws {
6843+ var container = encoder.singleValueContainer()
6844+ switch self {
6845+ case .string(let x):
6846+ try container.encode(x)
6847+ case .unionArray(let x):
6848+ try container.encode(x)
6849+ }
6850+ }
6851+}
6852+
6853+/// A string indicating a color [scheme](scale.html#scheme) name (e.g., `"category10"` or
6854+/// `"viridis"`) or a [scheme parameter object](scale.html#scheme-params).
6855+///
6856+/// Discrete color schemes may be used with [discrete](scale.html#discrete) or
6857+/// [discretizing](scale.html#discretizing) scales. Continuous color schemes are intended for
6858+/// use with [sequential](scales.html#sequential) scales.
6859+///
6860+/// For the full list of supported scheme, please refer to the [Vega
6861+/// Scheme](https://vega.github.io/vega/docs/schemes/#reference) reference.
6862+enum Scheme: Codable {
6863+ case schemeParams(SchemeParams)
6864+ case string(String)
6865+
6866+ init(from decoder: Decoder) throws {
6867+ let container = try decoder.singleValueContainer()
6868+ if let x = try? container.decode(String.self) {
6869+ self = .string(x)
6870+ return
6871+ }
6872+ if let x = try? container.decode(SchemeParams.self) {
6873+ self = .schemeParams(x)
6874+ return
6875+ }
6876+ throw DecodingError.typeMismatch(Scheme.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Scheme"))
6877+ }
6878+
6879+ func encode(to encoder: Encoder) throws {
6880+ var container = encoder.singleValueContainer()
6881+ switch self {
6882+ case .schemeParams(let x):
6883+ try container.encode(x)
6884+ case .string(let x):
6885+ try container.encode(x)
6886+ }
6887+ }
6888+}
6889+
6890+// MARK: - SchemeParams
6891+struct SchemeParams: Codable {
6892+ /// For sequential and diverging schemes only, determines the extent of the color range to
6893+ /// use. For example `[0.2, 1]` will rescale the color scheme such that color values in the
6894+ /// range _[0, 0.2)_ are excluded from the scheme.
6895+ let extent: [Double]?
6896+ /// A color scheme name for sequential/ordinal scales (e.g., `"category10"` or `"viridis"`).
6897+ ///
6898+ /// For the full list of supported scheme, please refer to the [Vega
6899+ /// Scheme](https://vega.github.io/vega/docs/schemes/#reference) reference.
6900+ let name: String
6901+
6902+ enum CodingKeys: String, CodingKey {
6903+ case extent = "extent"
6904+ case name = "name"
6905+ }
6906+}
6907+
6908+// MARK: SchemeParams convenience initializers and mutators
6909+
6910+extension SchemeParams {
6911+ init(data: Data) throws {
6912+ self = try newJSONDecoder().decode(SchemeParams.self, from: data)
6913+ }
6914+
6915+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
6916+ guard let data = json.data(using: encoding) else {
6917+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
6918+ }
6919+ try self.init(data: data)
6920+ }
6921+
6922+ init(fromURL url: URL) throws {
6923+ try self.init(data: try Data(contentsOf: url))
6924+ }
6925+
6926+ func with(
6927+ extent: [Double]?? = nil,
6928+ name: String? = nil
6929+ ) -> SchemeParams {
6930+ return SchemeParams(
6931+ extent: extent ?? self.extent,
6932+ name: name ?? self.name
6933+ )
6934+ }
6935+
6936+ func jsonData() throws -> Data {
6937+ return try newJSONEncoder().encode(self)
6938+ }
6939+
6940+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
6941+ return String(data: try self.jsonData(), encoding: encoding)
6942+ }
6943+}
6944+
6945+/// The type of scale. Vega-Lite supports the following categories of scale types:
6946+///
6947+/// 1) [**Continuous Scales**](scale.html#continuous) -- mapping continuous domains to
6948+/// continuous output ranges ([`"linear"`](scale.html#linear), [`"pow"`](scale.html#pow),
6949+/// [`"sqrt"`](scale.html#sqrt), [`"log"`](scale.html#log), [`"time"`](scale.html#time),
6950+/// [`"utc"`](scale.html#utc), [`"sequential"`](scale.html#sequential)).
6951+///
6952+/// 2) [**Discrete Scales**](scale.html#discrete) -- mapping discrete domains to discrete
6953+/// ([`"ordinal"`](scale.html#ordinal)) or continuous ([`"band"`](scale.html#band) and
6954+/// [`"point"`](scale.html#point)) output ranges.
6955+///
6956+/// 3) [**Discretizing Scales**](scale.html#discretizing) -- mapping continuous domains to
6957+/// discrete output ranges ([`"bin-linear"`](scale.html#bin-linear) and
6958+/// [`"bin-ordinal"`](scale.html#bin-ordinal)).
6959+///
6960+/// __Default value:__ please see the [scale type table](scale.html#type).
6961+enum ScaleType: String, Codable {
6962+ case linear = "linear"
6963+ case binLinear = "bin-linear"
6964+ case log = "log"
6965+ case pow = "pow"
6966+ case sqrt = "sqrt"
6967+ case time = "time"
6968+ case utc = "utc"
6969+ case sequential = "sequential"
6970+ case ordinal = "ordinal"
6971+ case binOrdinal = "bin-ordinal"
6972+ case point = "point"
6973+ case band = "band"
6974+}
6975+
6976+enum SortUnion: Codable {
6977+ case enumeration(SortEnum)
6978+ case sortField(SortField)
6979+ case null
6980+
6981+ init(from decoder: Decoder) throws {
6982+ let container = try decoder.singleValueContainer()
6983+ if let x = try? container.decode(SortEnum.self) {
6984+ self = .enumeration(x)
6985+ return
6986+ }
6987+ if let x = try? container.decode(SortField.self) {
6988+ self = .sortField(x)
6989+ return
6990+ }
6991+ if container.decodeNil() {
6992+ self = .null
6993+ return
6994+ }
6995+ throw DecodingError.typeMismatch(SortUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for SortUnion"))
6996+ }
6997+
6998+ func encode(to encoder: Encoder) throws {
6999+ var container = encoder.singleValueContainer()
7000+ switch self {
7001+ case .enumeration(let x):
7002+ try container.encode(x)
7003+ case .sortField(let x):
7004+ try container.encode(x)
7005+ case .null:
7006+ try container.encodeNil()
7007+ }
7008+ }
7009+}
7010+
7011+// MARK: - SortField
7012+struct SortField: Codable {
7013+ /// The data [field](field.html) to sort by.
7014+ ///
7015+ /// __Default value:__ If unspecified, defaults to the field specified in the outer data
7016+ /// reference.
7017+ let field: Field?
7018+ /// An [aggregate operation](aggregate.html#ops) to perform on the field prior to sorting
7019+ /// (e.g., `"count"`, `"mean"` and `"median"`).
7020+ /// This property is required in cases where the sort field and the data reference field do
7021+ /// not match.
7022+ /// The input data objects will be aggregated, grouped by the encoded data field.
7023+ ///
7024+ /// For a full list of operations, please see the documentation for
7025+ /// [aggregate](aggregate.html#ops).
7026+ let op: AggregateOp
7027+ /// The sort order. One of `"ascending"` (default) or `"descending"`.
7028+ let order: SortEnum?
7029+
7030+ enum CodingKeys: String, CodingKey {
7031+ case field = "field"
7032+ case op = "op"
7033+ case order = "order"
7034+ }
7035+}
7036+
7037+// MARK: SortField convenience initializers and mutators
7038+
7039+extension SortField {
7040+ init(data: Data) throws {
7041+ self = try newJSONDecoder().decode(SortField.self, from: data)
7042+ }
7043+
7044+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7045+ guard let data = json.data(using: encoding) else {
7046+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7047+ }
7048+ try self.init(data: data)
7049+ }
7050+
7051+ init(fromURL url: URL) throws {
7052+ try self.init(data: try Data(contentsOf: url))
7053+ }
7054+
7055+ func with(
7056+ field: Field?? = nil,
7057+ op: AggregateOp? = nil,
7058+ order: SortEnum?? = nil
7059+ ) -> SortField {
7060+ return SortField(
7061+ field: field ?? self.field,
7062+ op: op ?? self.op,
7063+ order: order ?? self.order
7064+ )
7065+ }
7066+
7067+ func jsonData() throws -> Data {
7068+ return try newJSONEncoder().encode(self)
7069+ }
7070+
7071+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7072+ return String(data: try self.jsonData(), encoding: encoding)
7073+ }
7074+}
7075+
7076+enum SortEnum: String, Codable {
7077+ case ascending = "ascending"
7078+ case descending = "descending"
7079+}
7080+
7081+/// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7082+/// `"nominal"`).
7083+/// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7084+/// [geographic projection](projection.html) is applied.
7085+///
7086+/// Constants and utilities for data type
7087+/// Data type based on level of measurement
7088+enum ConditionalPredicateValueDefType: String, Codable {
7089+ case quantitative = "quantitative"
7090+ case ordinal = "ordinal"
7091+ case temporal = "temporal"
7092+ case nominal = "nominal"
7093+ case latitude = "latitude"
7094+ case longitude = "longitude"
7095+ case geojson = "geojson"
7096+}
7097+
7098+/// Horizontal facets for trellis plots.
7099+///
7100+/// Vertical facets for trellis plots.
7101+// MARK: - FacetFieldDef
7102+struct FacetFieldDef: Codable {
7103+ /// Aggregation function for the field
7104+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7105+ ///
7106+ /// __Default value:__ `undefined` (None)
7107+ let aggregate: AggregateOp?
7108+ /// A flag for binning a `quantitative` field, or [an object defining binning
7109+ /// parameters](bin.html#params).
7110+ /// If `true`, default [binning parameters](bin.html) will be applied.
7111+ ///
7112+ /// __Default value:__ `false`
7113+ let bin: Bin?
7114+ /// __Required.__ A string defining the name of the field from which to pull a data value
7115+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7116+ ///
7117+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7118+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7119+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7120+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7121+ /// See more details about escaping in the [field documentation](field.html).
7122+ ///
7123+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7124+ let field: Field?
7125+ /// An object defining properties of a facet's header.
7126+ let header: Header?
7127+ /// Sort order for a facet field.
7128+ /// This can be `"ascending"`, `"descending"`.
7129+ let sort: SortEnum?
7130+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7131+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7132+ ///
7133+ /// __Default value:__ `undefined` (None)
7134+ let timeUnit: TimeUnit?
7135+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7136+ /// `"nominal"`).
7137+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7138+ /// [geographic projection](projection.html) is applied.
7139+ let type: ConditionalPredicateValueDefType
7140+
7141+ enum CodingKeys: String, CodingKey {
7142+ case aggregate = "aggregate"
7143+ case bin = "bin"
7144+ case field = "field"
7145+ case header = "header"
7146+ case sort = "sort"
7147+ case timeUnit = "timeUnit"
7148+ case type = "type"
7149+ }
7150+}
7151+
7152+// MARK: FacetFieldDef convenience initializers and mutators
7153+
7154+extension FacetFieldDef {
7155+ init(data: Data) throws {
7156+ self = try newJSONDecoder().decode(FacetFieldDef.self, from: data)
7157+ }
7158+
7159+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7160+ guard let data = json.data(using: encoding) else {
7161+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7162+ }
7163+ try self.init(data: data)
7164+ }
7165+
7166+ init(fromURL url: URL) throws {
7167+ try self.init(data: try Data(contentsOf: url))
7168+ }
7169+
7170+ func with(
7171+ aggregate: AggregateOp?? = nil,
7172+ bin: Bin?? = nil,
7173+ field: Field?? = nil,
7174+ header: Header?? = nil,
7175+ sort: SortEnum?? = nil,
7176+ timeUnit: TimeUnit?? = nil,
7177+ type: ConditionalPredicateValueDefType? = nil
7178+ ) -> FacetFieldDef {
7179+ return FacetFieldDef(
7180+ aggregate: aggregate ?? self.aggregate,
7181+ bin: bin ?? self.bin,
7182+ field: field ?? self.field,
7183+ header: header ?? self.header,
7184+ sort: sort ?? self.sort,
7185+ timeUnit: timeUnit ?? self.timeUnit,
7186+ type: type ?? self.type
7187+ )
7188+ }
7189+
7190+ func jsonData() throws -> Data {
7191+ return try newJSONEncoder().encode(self)
7192+ }
7193+
7194+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7195+ return String(data: try self.jsonData(), encoding: encoding)
7196+ }
7197+}
7198+
7199+/// An object defining properties of a facet's header.
7200+///
7201+/// Headers of row / column channels for faceted plots.
7202+// MARK: - Header
7203+struct Header: Codable {
7204+ /// The formatting pattern for labels. This is D3's [number format
7205+ /// pattern](https://github.com/d3/d3-format#locale_format) for quantitative fields and D3's
7206+ /// [time format pattern](https://github.com/d3/d3-time-format#locale_format) for time
7207+ /// field.
7208+ ///
7209+ /// See the [format documentation](format.html) for more information.
7210+ ///
7211+ /// __Default value:__ derived from [numberFormat](config.html#format) config for
7212+ /// quantitative fields and from [timeFormat](config.html#format) config for temporal fields.
7213+ let format: String?
7214+ /// The rotation angle of the header labels.
7215+ ///
7216+ /// __Default value:__ `0`.
7217+ let labelAngle: Double?
7218+ /// A title for the field. If `null`, the title will be removed.
7219+ ///
7220+ /// __Default value:__ derived from the field's name and transformation function
7221+ /// (`aggregate`, `bin` and `timeUnit`). If the field has an aggregate function, the
7222+ /// function is displayed as a part of the title (e.g., `"Sum of Profit"`). If the field is
7223+ /// binned or has a time unit applied, the applied function will be denoted in parentheses
7224+ /// (e.g., `"Profit (binned)"`, `"Transaction Date (year-month)"`). Otherwise, the title is
7225+ /// simply the field name.
7226+ ///
7227+ /// __Note__: You can customize the default field title format by providing the [`fieldTitle`
7228+ /// property in the [config](config.html) or [`fieldTitle` function via the `compile`
7229+ /// function's options](compile.html#field-title).
7230+ let title: String?
7231+
7232+ enum CodingKeys: String, CodingKey {
7233+ case format = "format"
7234+ case labelAngle = "labelAngle"
7235+ case title = "title"
7236+ }
7237+}
7238+
7239+// MARK: Header convenience initializers and mutators
7240+
7241+extension Header {
7242+ init(data: Data) throws {
7243+ self = try newJSONDecoder().decode(Header.self, from: data)
7244+ }
7245+
7246+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7247+ guard let data = json.data(using: encoding) else {
7248+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7249+ }
7250+ try self.init(data: data)
7251+ }
7252+
7253+ init(fromURL url: URL) throws {
7254+ try self.init(data: try Data(contentsOf: url))
7255+ }
7256+
7257+ func with(
7258+ format: String?? = nil,
7259+ labelAngle: Double?? = nil,
7260+ title: String?? = nil
7261+ ) -> Header {
7262+ return Header(
7263+ format: format ?? self.format,
7264+ labelAngle: labelAngle ?? self.labelAngle,
7265+ title: title ?? self.title
7266+ )
7267+ }
7268+
7269+ func jsonData() throws -> Data {
7270+ return try newJSONEncoder().encode(self)
7271+ }
7272+
7273+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7274+ return String(data: try self.jsonData(), encoding: encoding)
7275+ }
7276+}
7277+
7278+enum Detail: Codable {
7279+ case fieldDef(FieldDef)
7280+ case fieldDefArray([FieldDef])
7281+
7282+ init(from decoder: Decoder) throws {
7283+ let container = try decoder.singleValueContainer()
7284+ if let x = try? container.decode([FieldDef].self) {
7285+ self = .fieldDefArray(x)
7286+ return
7287+ }
7288+ if let x = try? container.decode(FieldDef.self) {
7289+ self = .fieldDef(x)
7290+ return
7291+ }
7292+ throw DecodingError.typeMismatch(Detail.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Detail"))
7293+ }
7294+
7295+ func encode(to encoder: Encoder) throws {
7296+ var container = encoder.singleValueContainer()
7297+ switch self {
7298+ case .fieldDef(let x):
7299+ try container.encode(x)
7300+ case .fieldDefArray(let x):
7301+ try container.encode(x)
7302+ }
7303+ }
7304+}
7305+
7306+/// Definition object for a data field, its type and transformation of an encoding channel.
7307+// MARK: - FieldDef
7308+struct FieldDef: Codable {
7309+ /// Aggregation function for the field
7310+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7311+ ///
7312+ /// __Default value:__ `undefined` (None)
7313+ let aggregate: AggregateOp?
7314+ /// A flag for binning a `quantitative` field, or [an object defining binning
7315+ /// parameters](bin.html#params).
7316+ /// If `true`, default [binning parameters](bin.html) will be applied.
7317+ ///
7318+ /// __Default value:__ `false`
7319+ let bin: Bin?
7320+ /// __Required.__ A string defining the name of the field from which to pull a data value
7321+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7322+ ///
7323+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7324+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7325+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7326+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7327+ /// See more details about escaping in the [field documentation](field.html).
7328+ ///
7329+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7330+ let field: Field?
7331+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7332+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7333+ ///
7334+ /// __Default value:__ `undefined` (None)
7335+ let timeUnit: TimeUnit?
7336+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7337+ /// `"nominal"`).
7338+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7339+ /// [geographic projection](projection.html) is applied.
7340+ let type: ConditionalPredicateValueDefType
7341+
7342+ enum CodingKeys: String, CodingKey {
7343+ case aggregate = "aggregate"
7344+ case bin = "bin"
7345+ case field = "field"
7346+ case timeUnit = "timeUnit"
7347+ case type = "type"
7348+ }
7349+}
7350+
7351+// MARK: FieldDef convenience initializers and mutators
7352+
7353+extension FieldDef {
7354+ init(data: Data) throws {
7355+ self = try newJSONDecoder().decode(FieldDef.self, from: data)
7356+ }
7357+
7358+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7359+ guard let data = json.data(using: encoding) else {
7360+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7361+ }
7362+ try self.init(data: data)
7363+ }
7364+
7365+ init(fromURL url: URL) throws {
7366+ try self.init(data: try Data(contentsOf: url))
7367+ }
7368+
7369+ func with(
7370+ aggregate: AggregateOp?? = nil,
7371+ bin: Bin?? = nil,
7372+ field: Field?? = nil,
7373+ timeUnit: TimeUnit?? = nil,
7374+ type: ConditionalPredicateValueDefType? = nil
7375+ ) -> FieldDef {
7376+ return FieldDef(
7377+ aggregate: aggregate ?? self.aggregate,
7378+ bin: bin ?? self.bin,
7379+ field: field ?? self.field,
7380+ timeUnit: timeUnit ?? self.timeUnit,
7381+ type: type ?? self.type
7382+ )
7383+ }
7384+
7385+ func jsonData() throws -> Data {
7386+ return try newJSONEncoder().encode(self)
7387+ }
7388+
7389+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7390+ return String(data: try self.jsonData(), encoding: encoding)
7391+ }
7392+}
7393+
7394+/// A URL to load upon mouse click.
7395+///
7396+/// A FieldDef with Condition<ValueDef>
7397+/// {
7398+/// condition: {value: ...},
7399+/// field: ...,
7400+/// ...
7401+/// }
7402+///
7403+/// A ValueDef with Condition<ValueDef | FieldDef>
7404+/// {
7405+/// condition: {field: ...} | {value: ...},
7406+/// value: ...,
7407+/// }
7408+// MARK: - DefWithCondition
7409+struct DefWithCondition: Codable {
7410+ /// Aggregation function for the field
7411+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7412+ ///
7413+ /// __Default value:__ `undefined` (None)
7414+ let aggregate: AggregateOp?
7415+ /// A flag for binning a `quantitative` field, or [an object defining binning
7416+ /// parameters](bin.html#params).
7417+ /// If `true`, default [binning parameters](bin.html) will be applied.
7418+ ///
7419+ /// __Default value:__ `false`
7420+ let bin: Bin?
7421+ /// One or more value definition(s) with a selection predicate.
7422+ ///
7423+ /// __Note:__ A field definition's `condition` property can only contain [value
7424+ /// definitions](encoding.html#value-def)
7425+ /// since Vega-Lite only allows at most one encoded field per encoding channel.
7426+ ///
7427+ /// A field definition or one or more value definition(s) with a selection predicate.
7428+ let condition: HrefCondition?
7429+ /// __Required.__ A string defining the name of the field from which to pull a data value
7430+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7431+ ///
7432+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7433+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7434+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7435+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7436+ /// See more details about escaping in the [field documentation](field.html).
7437+ ///
7438+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7439+ let field: Field?
7440+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7441+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7442+ ///
7443+ /// __Default value:__ `undefined` (None)
7444+ let timeUnit: TimeUnit?
7445+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7446+ /// `"nominal"`).
7447+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7448+ /// [geographic projection](projection.html) is applied.
7449+ let type: ConditionalPredicateValueDefType?
7450+ /// A constant value in visual domain.
7451+ let value: ConditionalValueDefValue?
7452+
7453+ enum CodingKeys: String, CodingKey {
7454+ case aggregate = "aggregate"
7455+ case bin = "bin"
7456+ case condition = "condition"
7457+ case field = "field"
7458+ case timeUnit = "timeUnit"
7459+ case type = "type"
7460+ case value = "value"
7461+ }
7462+}
7463+
7464+// MARK: DefWithCondition convenience initializers and mutators
7465+
7466+extension DefWithCondition {
7467+ init(data: Data) throws {
7468+ self = try newJSONDecoder().decode(DefWithCondition.self, from: data)
7469+ }
7470+
7471+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7472+ guard let data = json.data(using: encoding) else {
7473+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7474+ }
7475+ try self.init(data: data)
7476+ }
7477+
7478+ init(fromURL url: URL) throws {
7479+ try self.init(data: try Data(contentsOf: url))
7480+ }
7481+
7482+ func with(
7483+ aggregate: AggregateOp?? = nil,
7484+ bin: Bin?? = nil,
7485+ condition: HrefCondition?? = nil,
7486+ field: Field?? = nil,
7487+ timeUnit: TimeUnit?? = nil,
7488+ type: ConditionalPredicateValueDefType?? = nil,
7489+ value: ConditionalValueDefValue?? = nil
7490+ ) -> DefWithCondition {
7491+ return DefWithCondition(
7492+ aggregate: aggregate ?? self.aggregate,
7493+ bin: bin ?? self.bin,
7494+ condition: condition ?? self.condition,
7495+ field: field ?? self.field,
7496+ timeUnit: timeUnit ?? self.timeUnit,
7497+ type: type ?? self.type,
7498+ value: value ?? self.value
7499+ )
7500+ }
7501+
7502+ func jsonData() throws -> Data {
7503+ return try newJSONEncoder().encode(self)
7504+ }
7505+
7506+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7507+ return String(data: try self.jsonData(), encoding: encoding)
7508+ }
7509+}
7510+
7511+enum HrefCondition: Codable {
7512+ case conditionalPredicateFieldDefClass(ConditionalPredicateFieldDefClass)
7513+ case conditionalValueDefArray([ConditionalValueDef])
7514+
7515+ init(from decoder: Decoder) throws {
7516+ let container = try decoder.singleValueContainer()
7517+ if let x = try? container.decode([ConditionalValueDef].self) {
7518+ self = .conditionalValueDefArray(x)
7519+ return
7520+ }
7521+ if let x = try? container.decode(ConditionalPredicateFieldDefClass.self) {
7522+ self = .conditionalPredicateFieldDefClass(x)
7523+ return
7524+ }
7525+ throw DecodingError.typeMismatch(HrefCondition.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for HrefCondition"))
7526+ }
7527+
7528+ func encode(to encoder: Encoder) throws {
7529+ var container = encoder.singleValueContainer()
7530+ switch self {
7531+ case .conditionalPredicateFieldDefClass(let x):
7532+ try container.encode(x)
7533+ case .conditionalValueDefArray(let x):
7534+ try container.encode(x)
7535+ }
7536+ }
7537+}
7538+
7539+// MARK: - ConditionalPredicateFieldDefClass
7540+struct ConditionalPredicateFieldDefClass: Codable {
7541+ let test: LogicalOperandPredicate?
7542+ /// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
7543+ /// `0` to `1` for opacity).
7544+ let value: ConditionalValueDefValue?
7545+ /// A [selection name](selection.html), or a series of [composed
7546+ /// selections](selection.html#compose).
7547+ let selection: SelectionOperand?
7548+ /// Aggregation function for the field
7549+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7550+ ///
7551+ /// __Default value:__ `undefined` (None)
7552+ let aggregate: AggregateOp?
7553+ /// A flag for binning a `quantitative` field, or [an object defining binning
7554+ /// parameters](bin.html#params).
7555+ /// If `true`, default [binning parameters](bin.html) will be applied.
7556+ ///
7557+ /// __Default value:__ `false`
7558+ let bin: Bin?
7559+ /// __Required.__ A string defining the name of the field from which to pull a data value
7560+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7561+ ///
7562+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7563+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7564+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7565+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7566+ /// See more details about escaping in the [field documentation](field.html).
7567+ ///
7568+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7569+ let field: Field?
7570+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7571+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7572+ ///
7573+ /// __Default value:__ `undefined` (None)
7574+ let timeUnit: TimeUnit?
7575+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7576+ /// `"nominal"`).
7577+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7578+ /// [geographic projection](projection.html) is applied.
7579+ let type: ConditionalPredicateValueDefType?
7580+
7581+ enum CodingKeys: String, CodingKey {
7582+ case test = "test"
7583+ case value = "value"
7584+ case selection = "selection"
7585+ case aggregate = "aggregate"
7586+ case bin = "bin"
7587+ case field = "field"
7588+ case timeUnit = "timeUnit"
7589+ case type = "type"
7590+ }
7591+}
7592+
7593+// MARK: ConditionalPredicateFieldDefClass convenience initializers and mutators
7594+
7595+extension ConditionalPredicateFieldDefClass {
7596+ init(data: Data) throws {
7597+ self = try newJSONDecoder().decode(ConditionalPredicateFieldDefClass.self, from: data)
7598+ }
7599+
7600+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7601+ guard let data = json.data(using: encoding) else {
7602+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7603+ }
7604+ try self.init(data: data)
7605+ }
7606+
7607+ init(fromURL url: URL) throws {
7608+ try self.init(data: try Data(contentsOf: url))
7609+ }
7610+
7611+ func with(
7612+ test: LogicalOperandPredicate?? = nil,
7613+ value: ConditionalValueDefValue?? = nil,
7614+ selection: SelectionOperand?? = nil,
7615+ aggregate: AggregateOp?? = nil,
7616+ bin: Bin?? = nil,
7617+ field: Field?? = nil,
7618+ timeUnit: TimeUnit?? = nil,
7619+ type: ConditionalPredicateValueDefType?? = nil
7620+ ) -> ConditionalPredicateFieldDefClass {
7621+ return ConditionalPredicateFieldDefClass(
7622+ test: test ?? self.test,
7623+ value: value ?? self.value,
7624+ selection: selection ?? self.selection,
7625+ aggregate: aggregate ?? self.aggregate,
7626+ bin: bin ?? self.bin,
7627+ field: field ?? self.field,
7628+ timeUnit: timeUnit ?? self.timeUnit,
7629+ type: type ?? self.type
7630+ )
7631+ }
7632+
7633+ func jsonData() throws -> Data {
7634+ return try newJSONEncoder().encode(self)
7635+ }
7636+
7637+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7638+ return String(data: try self.jsonData(), encoding: encoding)
7639+ }
7640+}
7641+
7642+enum Order: Codable {
7643+ case orderFieldDef(OrderFieldDef)
7644+ case orderFieldDefArray([OrderFieldDef])
7645+
7646+ init(from decoder: Decoder) throws {
7647+ let container = try decoder.singleValueContainer()
7648+ if let x = try? container.decode([OrderFieldDef].self) {
7649+ self = .orderFieldDefArray(x)
7650+ return
7651+ }
7652+ if let x = try? container.decode(OrderFieldDef.self) {
7653+ self = .orderFieldDef(x)
7654+ return
7655+ }
7656+ throw DecodingError.typeMismatch(Order.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Order"))
7657+ }
7658+
7659+ func encode(to encoder: Encoder) throws {
7660+ var container = encoder.singleValueContainer()
7661+ switch self {
7662+ case .orderFieldDef(let x):
7663+ try container.encode(x)
7664+ case .orderFieldDefArray(let x):
7665+ try container.encode(x)
7666+ }
7667+ }
7668+}
7669+
7670+// MARK: - OrderFieldDef
7671+struct OrderFieldDef: Codable {
7672+ /// Aggregation function for the field
7673+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7674+ ///
7675+ /// __Default value:__ `undefined` (None)
7676+ let aggregate: AggregateOp?
7677+ /// A flag for binning a `quantitative` field, or [an object defining binning
7678+ /// parameters](bin.html#params).
7679+ /// If `true`, default [binning parameters](bin.html) will be applied.
7680+ ///
7681+ /// __Default value:__ `false`
7682+ let bin: Bin?
7683+ /// __Required.__ A string defining the name of the field from which to pull a data value
7684+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7685+ ///
7686+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7687+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7688+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7689+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7690+ /// See more details about escaping in the [field documentation](field.html).
7691+ ///
7692+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7693+ let field: Field?
7694+ /// The sort order. One of `"ascending"` (default) or `"descending"`.
7695+ let sort: SortEnum?
7696+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7697+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7698+ ///
7699+ /// __Default value:__ `undefined` (None)
7700+ let timeUnit: TimeUnit?
7701+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7702+ /// `"nominal"`).
7703+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7704+ /// [geographic projection](projection.html) is applied.
7705+ let type: ConditionalPredicateValueDefType
7706+
7707+ enum CodingKeys: String, CodingKey {
7708+ case aggregate = "aggregate"
7709+ case bin = "bin"
7710+ case field = "field"
7711+ case sort = "sort"
7712+ case timeUnit = "timeUnit"
7713+ case type = "type"
7714+ }
7715+}
7716+
7717+// MARK: OrderFieldDef convenience initializers and mutators
7718+
7719+extension OrderFieldDef {
7720+ init(data: Data) throws {
7721+ self = try newJSONDecoder().decode(OrderFieldDef.self, from: data)
7722+ }
7723+
7724+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7725+ guard let data = json.data(using: encoding) else {
7726+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7727+ }
7728+ try self.init(data: data)
7729+ }
7730+
7731+ init(fromURL url: URL) throws {
7732+ try self.init(data: try Data(contentsOf: url))
7733+ }
7734+
7735+ func with(
7736+ aggregate: AggregateOp?? = nil,
7737+ bin: Bin?? = nil,
7738+ field: Field?? = nil,
7739+ sort: SortEnum?? = nil,
7740+ timeUnit: TimeUnit?? = nil,
7741+ type: ConditionalPredicateValueDefType? = nil
7742+ ) -> OrderFieldDef {
7743+ return OrderFieldDef(
7744+ aggregate: aggregate ?? self.aggregate,
7745+ bin: bin ?? self.bin,
7746+ field: field ?? self.field,
7747+ sort: sort ?? self.sort,
7748+ timeUnit: timeUnit ?? self.timeUnit,
7749+ type: type ?? self.type
7750+ )
7751+ }
7752+
7753+ func jsonData() throws -> Data {
7754+ return try newJSONEncoder().encode(self)
7755+ }
7756+
7757+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7758+ return String(data: try self.jsonData(), encoding: encoding)
7759+ }
7760+}
7761+
7762+/// Text of the `text` mark.
7763+///
7764+/// The tooltip text to show upon mouse hover.
7765+///
7766+/// A FieldDef with Condition<ValueDef>
7767+/// {
7768+/// condition: {value: ...},
7769+/// field: ...,
7770+/// ...
7771+/// }
7772+///
7773+/// A ValueDef with Condition<ValueDef | FieldDef>
7774+/// {
7775+/// condition: {field: ...} | {value: ...},
7776+/// value: ...,
7777+/// }
7778+// MARK: - TextDefWithCondition
7779+struct TextDefWithCondition: Codable {
7780+ /// Aggregation function for the field
7781+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7782+ ///
7783+ /// __Default value:__ `undefined` (None)
7784+ let aggregate: AggregateOp?
7785+ /// A flag for binning a `quantitative` field, or [an object defining binning
7786+ /// parameters](bin.html#params).
7787+ /// If `true`, default [binning parameters](bin.html) will be applied.
7788+ ///
7789+ /// __Default value:__ `false`
7790+ let bin: Bin?
7791+ /// One or more value definition(s) with a selection predicate.
7792+ ///
7793+ /// __Note:__ A field definition's `condition` property can only contain [value
7794+ /// definitions](encoding.html#value-def)
7795+ /// since Vega-Lite only allows at most one encoded field per encoding channel.
7796+ ///
7797+ /// A field definition or one or more value definition(s) with a selection predicate.
7798+ let condition: TextCondition?
7799+ /// __Required.__ A string defining the name of the field from which to pull a data value
7800+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7801+ ///
7802+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7803+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7804+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7805+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7806+ /// See more details about escaping in the [field documentation](field.html).
7807+ ///
7808+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7809+ let field: Field?
7810+ /// The [formatting pattern](format.html) for a text field. If not defined, this will be
7811+ /// determined automatically.
7812+ let format: String?
7813+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7814+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7815+ ///
7816+ /// __Default value:__ `undefined` (None)
7817+ let timeUnit: TimeUnit?
7818+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7819+ /// `"nominal"`).
7820+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7821+ /// [geographic projection](projection.html) is applied.
7822+ let type: ConditionalPredicateValueDefType?
7823+ /// A constant value in visual domain.
7824+ let value: ConditionalValueDefValue?
7825+
7826+ enum CodingKeys: String, CodingKey {
7827+ case aggregate = "aggregate"
7828+ case bin = "bin"
7829+ case condition = "condition"
7830+ case field = "field"
7831+ case format = "format"
7832+ case timeUnit = "timeUnit"
7833+ case type = "type"
7834+ case value = "value"
7835+ }
7836+}
7837+
7838+// MARK: TextDefWithCondition convenience initializers and mutators
7839+
7840+extension TextDefWithCondition {
7841+ init(data: Data) throws {
7842+ self = try newJSONDecoder().decode(TextDefWithCondition.self, from: data)
7843+ }
7844+
7845+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7846+ guard let data = json.data(using: encoding) else {
7847+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7848+ }
7849+ try self.init(data: data)
7850+ }
7851+
7852+ init(fromURL url: URL) throws {
7853+ try self.init(data: try Data(contentsOf: url))
7854+ }
7855+
7856+ func with(
7857+ aggregate: AggregateOp?? = nil,
7858+ bin: Bin?? = nil,
7859+ condition: TextCondition?? = nil,
7860+ field: Field?? = nil,
7861+ format: String?? = nil,
7862+ timeUnit: TimeUnit?? = nil,
7863+ type: ConditionalPredicateValueDefType?? = nil,
7864+ value: ConditionalValueDefValue?? = nil
7865+ ) -> TextDefWithCondition {
7866+ return TextDefWithCondition(
7867+ aggregate: aggregate ?? self.aggregate,
7868+ bin: bin ?? self.bin,
7869+ condition: condition ?? self.condition,
7870+ field: field ?? self.field,
7871+ format: format ?? self.format,
7872+ timeUnit: timeUnit ?? self.timeUnit,
7873+ type: type ?? self.type,
7874+ value: value ?? self.value
7875+ )
7876+ }
7877+
7878+ func jsonData() throws -> Data {
7879+ return try newJSONEncoder().encode(self)
7880+ }
7881+
7882+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
7883+ return String(data: try self.jsonData(), encoding: encoding)
7884+ }
7885+}
7886+
7887+enum TextCondition: Codable {
7888+ case conditionalPredicateTextFieldDefClass(ConditionalPredicateTextFieldDefClass)
7889+ case conditionalValueDefArray([ConditionalValueDef])
7890+
7891+ init(from decoder: Decoder) throws {
7892+ let container = try decoder.singleValueContainer()
7893+ if let x = try? container.decode([ConditionalValueDef].self) {
7894+ self = .conditionalValueDefArray(x)
7895+ return
7896+ }
7897+ if let x = try? container.decode(ConditionalPredicateTextFieldDefClass.self) {
7898+ self = .conditionalPredicateTextFieldDefClass(x)
7899+ return
7900+ }
7901+ throw DecodingError.typeMismatch(TextCondition.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for TextCondition"))
7902+ }
7903+
7904+ func encode(to encoder: Encoder) throws {
7905+ var container = encoder.singleValueContainer()
7906+ switch self {
7907+ case .conditionalPredicateTextFieldDefClass(let x):
7908+ try container.encode(x)
7909+ case .conditionalValueDefArray(let x):
7910+ try container.encode(x)
7911+ }
7912+ }
7913+}
7914+
7915+// MARK: - ConditionalPredicateTextFieldDefClass
7916+struct ConditionalPredicateTextFieldDefClass: Codable {
7917+ let test: LogicalOperandPredicate?
7918+ /// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
7919+ /// `0` to `1` for opacity).
7920+ let value: ConditionalValueDefValue?
7921+ /// A [selection name](selection.html), or a series of [composed
7922+ /// selections](selection.html#compose).
7923+ let selection: SelectionOperand?
7924+ /// Aggregation function for the field
7925+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
7926+ ///
7927+ /// __Default value:__ `undefined` (None)
7928+ let aggregate: AggregateOp?
7929+ /// A flag for binning a `quantitative` field, or [an object defining binning
7930+ /// parameters](bin.html#params).
7931+ /// If `true`, default [binning parameters](bin.html) will be applied.
7932+ ///
7933+ /// __Default value:__ `false`
7934+ let bin: Bin?
7935+ /// __Required.__ A string defining the name of the field from which to pull a data value
7936+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
7937+ ///
7938+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
7939+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
7940+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
7941+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
7942+ /// See more details about escaping in the [field documentation](field.html).
7943+ ///
7944+ /// __Note:__ `field` is not required if `aggregate` is `count`.
7945+ let field: Field?
7946+ /// The [formatting pattern](format.html) for a text field. If not defined, this will be
7947+ /// determined automatically.
7948+ let format: String?
7949+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
7950+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
7951+ ///
7952+ /// __Default value:__ `undefined` (None)
7953+ let timeUnit: TimeUnit?
7954+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
7955+ /// `"nominal"`).
7956+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
7957+ /// [geographic projection](projection.html) is applied.
7958+ let type: ConditionalPredicateValueDefType?
7959+
7960+ enum CodingKeys: String, CodingKey {
7961+ case test = "test"
7962+ case value = "value"
7963+ case selection = "selection"
7964+ case aggregate = "aggregate"
7965+ case bin = "bin"
7966+ case field = "field"
7967+ case format = "format"
7968+ case timeUnit = "timeUnit"
7969+ case type = "type"
7970+ }
7971+}
7972+
7973+// MARK: ConditionalPredicateTextFieldDefClass convenience initializers and mutators
7974+
7975+extension ConditionalPredicateTextFieldDefClass {
7976+ init(data: Data) throws {
7977+ self = try newJSONDecoder().decode(ConditionalPredicateTextFieldDefClass.self, from: data)
7978+ }
7979+
7980+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
7981+ guard let data = json.data(using: encoding) else {
7982+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
7983+ }
7984+ try self.init(data: data)
7985+ }
7986+
7987+ init(fromURL url: URL) throws {
7988+ try self.init(data: try Data(contentsOf: url))
7989+ }
7990+
7991+ func with(
7992+ test: LogicalOperandPredicate?? = nil,
7993+ value: ConditionalValueDefValue?? = nil,
7994+ selection: SelectionOperand?? = nil,
7995+ aggregate: AggregateOp?? = nil,
7996+ bin: Bin?? = nil,
7997+ field: Field?? = nil,
7998+ format: String?? = nil,
7999+ timeUnit: TimeUnit?? = nil,
8000+ type: ConditionalPredicateValueDefType?? = nil
8001+ ) -> ConditionalPredicateTextFieldDefClass {
8002+ return ConditionalPredicateTextFieldDefClass(
8003+ test: test ?? self.test,
8004+ value: value ?? self.value,
8005+ selection: selection ?? self.selection,
8006+ aggregate: aggregate ?? self.aggregate,
8007+ bin: bin ?? self.bin,
8008+ field: field ?? self.field,
8009+ format: format ?? self.format,
8010+ timeUnit: timeUnit ?? self.timeUnit,
8011+ type: type ?? self.type
8012+ )
8013+ }
8014+
8015+ func jsonData() throws -> Data {
8016+ return try newJSONEncoder().encode(self)
8017+ }
8018+
8019+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8020+ return String(data: try self.jsonData(), encoding: encoding)
8021+ }
8022+}
8023+
8024+/// X coordinates of the marks, or width of horizontal `"bar"` and `"area"`.
8025+///
8026+/// Y coordinates of the marks, or height of vertical `"bar"` and `"area"`.
8027+///
8028+/// Definition object for a constant value of an encoding channel.
8029+// MARK: - XClass
8030+struct XClass: Codable {
8031+ /// Aggregation function for the field
8032+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
8033+ ///
8034+ /// __Default value:__ `undefined` (None)
8035+ let aggregate: AggregateOp?
8036+ /// An object defining properties of axis's gridlines, ticks and labels.
8037+ /// If `null`, the axis for the encoding channel will be removed.
8038+ ///
8039+ /// __Default value:__ If undefined, default [axis properties](axis.html) are applied.
8040+ let axis: Axis?
8041+ /// A flag for binning a `quantitative` field, or [an object defining binning
8042+ /// parameters](bin.html#params).
8043+ /// If `true`, default [binning parameters](bin.html) will be applied.
8044+ ///
8045+ /// __Default value:__ `false`
8046+ let bin: Bin?
8047+ /// __Required.__ A string defining the name of the field from which to pull a data value
8048+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
8049+ ///
8050+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
8051+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
8052+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
8053+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
8054+ /// See more details about escaping in the [field documentation](field.html).
8055+ ///
8056+ /// __Note:__ `field` is not required if `aggregate` is `count`.
8057+ let field: Field?
8058+ /// An object defining properties of the channel's scale, which is the function that
8059+ /// transforms values in the data domain (numbers, dates, strings, etc) to visual values
8060+ /// (pixels, colors, sizes) of the encoding channels.
8061+ ///
8062+ /// __Default value:__ If undefined, default [scale properties](scale.html) are applied.
8063+ let scale: Scale?
8064+ /// Sort order for the encoded field.
8065+ /// Supported `sort` values include `"ascending"`, `"descending"` and `null` (no sorting).
8066+ /// For fields with discrete domains, `sort` can also be a [sort field definition
8067+ /// object](sort.html#sort-field).
8068+ ///
8069+ /// __Default value:__ `"ascending"`
8070+ let sort: SortUnion?
8071+ /// Type of stacking offset if the field should be stacked.
8072+ /// `stack` is only applicable for `x` and `y` channels with continuous domains.
8073+ /// For example, `stack` of `y` can be used to customize stacking for a vertical bar chart.
8074+ ///
8075+ /// `stack` can be one of the following values:
8076+ ///
8077+ /// - `"zero"`: stacking with baseline offset at zero value of the scale (for creating
8078+ /// typical stacked [bar](stack.html#bar) and [area](stack.html#area) chart).
8079+ /// - `"normalize"` - stacking with normalized domain (for creating [normalized stacked bar
8080+ /// and area charts](stack.html#normalized). <br/>
8081+ /// - `"center"` - stacking with center baseline (for [streamgraph](stack.html#streamgraph)).
8082+ /// - `null` - No-stacking. This will produce layered [bar](stack.html#layered-bar-chart) and
8083+ /// area chart.
8084+ ///
8085+ /// __Default value:__ `zero` for plots with all of the following conditions are true:
8086+ ///
8087+ /// 1. The mark is `bar` or `area`;
8088+ /// 2. The stacked measure channel (x or y) has a linear scale;
8089+ /// 3. At least one of non-position channels mapped to an unaggregated field that is
8090+ /// different from x and y. Otherwise, `null` by default.
8091+ let stack: StackOffset?
8092+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
8093+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
8094+ ///
8095+ /// __Default value:__ `undefined` (None)
8096+ let timeUnit: TimeUnit?
8097+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
8098+ /// `"nominal"`).
8099+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
8100+ /// [geographic projection](projection.html) is applied.
8101+ let type: ConditionalPredicateValueDefType?
8102+ /// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
8103+ /// `0` to `1` for opacity).
8104+ let value: ConditionalValueDefValue?
8105+
8106+ enum CodingKeys: String, CodingKey {
8107+ case aggregate = "aggregate"
8108+ case axis = "axis"
8109+ case bin = "bin"
8110+ case field = "field"
8111+ case scale = "scale"
8112+ case sort = "sort"
8113+ case stack = "stack"
8114+ case timeUnit = "timeUnit"
8115+ case type = "type"
8116+ case value = "value"
8117+ }
8118+}
8119+
8120+// MARK: XClass convenience initializers and mutators
8121+
8122+extension XClass {
8123+ init(data: Data) throws {
8124+ self = try newJSONDecoder().decode(XClass.self, from: data)
8125+ }
8126+
8127+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
8128+ guard let data = json.data(using: encoding) else {
8129+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
8130+ }
8131+ try self.init(data: data)
8132+ }
8133+
8134+ init(fromURL url: URL) throws {
8135+ try self.init(data: try Data(contentsOf: url))
8136+ }
8137+
8138+ func with(
8139+ aggregate: AggregateOp?? = nil,
8140+ axis: Axis?? = nil,
8141+ bin: Bin?? = nil,
8142+ field: Field?? = nil,
8143+ scale: Scale?? = nil,
8144+ sort: SortUnion?? = nil,
8145+ stack: StackOffset?? = nil,
8146+ timeUnit: TimeUnit?? = nil,
8147+ type: ConditionalPredicateValueDefType?? = nil,
8148+ value: ConditionalValueDefValue?? = nil
8149+ ) -> XClass {
8150+ return XClass(
8151+ aggregate: aggregate ?? self.aggregate,
8152+ axis: axis ?? self.axis,
8153+ bin: bin ?? self.bin,
8154+ field: field ?? self.field,
8155+ scale: scale ?? self.scale,
8156+ sort: sort ?? self.sort,
8157+ stack: stack ?? self.stack,
8158+ timeUnit: timeUnit ?? self.timeUnit,
8159+ type: type ?? self.type,
8160+ value: value ?? self.value
8161+ )
8162+ }
8163+
8164+ func jsonData() throws -> Data {
8165+ return try newJSONEncoder().encode(self)
8166+ }
8167+
8168+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8169+ return String(data: try self.jsonData(), encoding: encoding)
8170+ }
8171+}
8172+
8173+// MARK: - Axis
8174+struct Axis: Codable {
8175+ /// A boolean flag indicating if the domain (the axis baseline) should be included as part of
8176+ /// the axis.
8177+ ///
8178+ /// __Default value:__ `true`
8179+ let domain: Bool?
8180+ /// The formatting pattern for labels. This is D3's [number format
8181+ /// pattern](https://github.com/d3/d3-format#locale_format) for quantitative fields and D3's
8182+ /// [time format pattern](https://github.com/d3/d3-time-format#locale_format) for time
8183+ /// field.
8184+ ///
8185+ /// See the [format documentation](format.html) for more information.
8186+ ///
8187+ /// __Default value:__ derived from [numberFormat](config.html#format) config for
8188+ /// quantitative fields and from [timeFormat](config.html#format) config for temporal fields.
8189+ let format: String?
8190+ /// A boolean flag indicating if grid lines should be included as part of the axis
8191+ ///
8192+ /// __Default value:__ `true` for [continuous scales](scale.html#continuous) that are not
8193+ /// binned; otherwise, `false`.
8194+ let grid: Bool?
8195+ /// The rotation angle of the axis labels.
8196+ ///
8197+ /// __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
8198+ let labelAngle: Double?
8199+ /// Indicates if labels should be hidden if they exceed the axis range. If `false `(the
8200+ /// default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
8201+ /// they exceed the axis range by more than 1 pixel. If this property is a number, it
8202+ /// specifies the pixel tolerance: the maximum amount by which a label bounding box may
8203+ /// exceed the axis range.
8204+ ///
8205+ /// __Default value:__ `false`.
8206+ let labelBound: Label?
8207+ /// Indicates if the first and last axis labels should be aligned flush with the scale range.
8208+ /// Flush alignment for a horizontal axis will left-align the first label and right-align the
8209+ /// last label. For vertical axes, bottom and top text baselines are applied instead. If this
8210+ /// property is a number, it also indicates the number of pixels by which to offset the first
8211+ /// and last labels; for example, a value of 2 will flush-align the first and last labels and
8212+ /// also push them 2 pixels outward from the center of the axis. The additional adjustment
8213+ /// can sometimes help the labels better visually group with corresponding axis ticks.
8214+ ///
8215+ /// __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
8216+ let labelFlush: Label?
8217+ /// The strategy to use for resolving overlap of axis labels. If `false` (the default), no
8218+ /// overlap reduction is attempted. If set to `true` or `"parity"`, a strategy of removing
8219+ /// every other label is used (this works well for standard linear axes). If set to
8220+ /// `"greedy"`, a linear scan of the labels is performed, removing any labels that overlaps
8221+ /// with the last visible label (this often works better for log-scaled axes).
8222+ ///
8223+ /// __Default value:__ `true` for non-nominal fields with non-log scales; `"greedy"` for log
8224+ /// scales; otherwise `false`.
8225+ let labelOverlap: LabelOverlapUnion?
8226+ /// The padding, in pixels, between axis and text labels.
8227+ let labelPadding: Double?
8228+ /// A boolean flag indicating if labels should be included as part of the axis.
8229+ ///
8230+ /// __Default value:__ `true`.
8231+ let labels: Bool?
8232+ /// The maximum extent in pixels that axis ticks and labels should use. This determines a
8233+ /// maximum offset value for axis titles.
8234+ ///
8235+ /// __Default value:__ `undefined`.
8236+ let maxExtent: Double?
8237+ /// The minimum extent in pixels that axis ticks and labels should use. This determines a
8238+ /// minimum offset value for axis titles.
8239+ ///
8240+ /// __Default value:__ `30` for y-axis; `undefined` for x-axis.
8241+ let minExtent: Double?
8242+ /// The offset, in pixels, by which to displace the axis from the edge of the enclosing group
8243+ /// or data rectangle.
8244+ ///
8245+ /// __Default value:__ derived from the [axis config](config.html#facet-scale-config)'s
8246+ /// `offset` (`0` by default)
8247+ let offset: Double?
8248+ /// The orientation of the axis. One of `"top"`, `"bottom"`, `"left"` or `"right"`. The
8249+ /// orientation can be used to further specialize the axis type (e.g., a y axis oriented for
8250+ /// the right edge of the chart).
8251+ ///
8252+ /// __Default value:__ `"bottom"` for x-axes and `"left"` for y-axes.
8253+ let orient: TitleOrient?
8254+ /// The anchor position of the axis in pixels. For x-axis with top or bottom orientation,
8255+ /// this sets the axis group x coordinate. For y-axis with left or right orientation, this
8256+ /// sets the axis group y coordinate.
8257+ ///
8258+ /// __Default value__: `0`
8259+ let position: Double?
8260+ /// A desired number of ticks, for axes visualizing quantitative scales. The resulting number
8261+ /// may be different so that values are "nice" (multiples of 2, 5, 10) and lie within the
8262+ /// underlying scale's range.
8263+ let tickCount: Double?
8264+ /// Boolean value that determines whether the axis should include ticks.
8265+ let ticks: Bool?
8266+ /// The size in pixels of axis ticks.
8267+ let tickSize: Double?
8268+ /// A title for the field. If `null`, the title will be removed.
8269+ ///
8270+ /// __Default value:__ derived from the field's name and transformation function
8271+ /// (`aggregate`, `bin` and `timeUnit`). If the field has an aggregate function, the
8272+ /// function is displayed as a part of the title (e.g., `"Sum of Profit"`). If the field is
8273+ /// binned or has a time unit applied, the applied function will be denoted in parentheses
8274+ /// (e.g., `"Profit (binned)"`, `"Transaction Date (year-month)"`). Otherwise, the title is
8275+ /// simply the field name.
8276+ ///
8277+ /// __Note__: You can customize the default field title format by providing the [`fieldTitle`
8278+ /// property in the [config](config.html) or [`fieldTitle` function via the `compile`
8279+ /// function's options](compile.html#field-title).
8280+ let title: String?
8281+ /// Max length for axis title if the title is automatically generated from the field's
8282+ /// description.
8283+ let titleMaxLength: Double?
8284+ /// The padding, in pixels, between title and axis.
8285+ let titlePadding: Double?
8286+ /// Explicitly set the visible axis tick values.
8287+ let values: [AxisValue]?
8288+ /// A non-positive integer indicating z-index of the axis.
8289+ /// If zindex is 0, axes should be drawn behind all chart elements.
8290+ /// To put them in front, use `"zindex = 1"`.
8291+ ///
8292+ /// __Default value:__ `1` (in front of the marks) for actual axis and `0` (behind the marks)
8293+ /// for grids.
8294+ let zindex: Double?
8295+
8296+ enum CodingKeys: String, CodingKey {
8297+ case domain = "domain"
8298+ case format = "format"
8299+ case grid = "grid"
8300+ case labelAngle = "labelAngle"
8301+ case labelBound = "labelBound"
8302+ case labelFlush = "labelFlush"
8303+ case labelOverlap = "labelOverlap"
8304+ case labelPadding = "labelPadding"
8305+ case labels = "labels"
8306+ case maxExtent = "maxExtent"
8307+ case minExtent = "minExtent"
8308+ case offset = "offset"
8309+ case orient = "orient"
8310+ case position = "position"
8311+ case tickCount = "tickCount"
8312+ case ticks = "ticks"
8313+ case tickSize = "tickSize"
8314+ case title = "title"
8315+ case titleMaxLength = "titleMaxLength"
8316+ case titlePadding = "titlePadding"
8317+ case values = "values"
8318+ case zindex = "zindex"
8319+ }
8320+}
8321+
8322+// MARK: Axis convenience initializers and mutators
8323+
8324+extension Axis {
8325+ init(data: Data) throws {
8326+ self = try newJSONDecoder().decode(Axis.self, from: data)
8327+ }
8328+
8329+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
8330+ guard let data = json.data(using: encoding) else {
8331+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
8332+ }
8333+ try self.init(data: data)
8334+ }
8335+
8336+ init(fromURL url: URL) throws {
8337+ try self.init(data: try Data(contentsOf: url))
8338+ }
8339+
8340+ func with(
8341+ domain: Bool?? = nil,
8342+ format: String?? = nil,
8343+ grid: Bool?? = nil,
8344+ labelAngle: Double?? = nil,
8345+ labelBound: Label?? = nil,
8346+ labelFlush: Label?? = nil,
8347+ labelOverlap: LabelOverlapUnion?? = nil,
8348+ labelPadding: Double?? = nil,
8349+ labels: Bool?? = nil,
8350+ maxExtent: Double?? = nil,
8351+ minExtent: Double?? = nil,
8352+ offset: Double?? = nil,
8353+ orient: TitleOrient?? = nil,
8354+ position: Double?? = nil,
8355+ tickCount: Double?? = nil,
8356+ ticks: Bool?? = nil,
8357+ tickSize: Double?? = nil,
8358+ title: String?? = nil,
8359+ titleMaxLength: Double?? = nil,
8360+ titlePadding: Double?? = nil,
8361+ values: [AxisValue]?? = nil,
8362+ zindex: Double?? = nil
8363+ ) -> Axis {
8364+ return Axis(
8365+ domain: domain ?? self.domain,
8366+ format: format ?? self.format,
8367+ grid: grid ?? self.grid,
8368+ labelAngle: labelAngle ?? self.labelAngle,
8369+ labelBound: labelBound ?? self.labelBound,
8370+ labelFlush: labelFlush ?? self.labelFlush,
8371+ labelOverlap: labelOverlap ?? self.labelOverlap,
8372+ labelPadding: labelPadding ?? self.labelPadding,
8373+ labels: labels ?? self.labels,
8374+ maxExtent: maxExtent ?? self.maxExtent,
8375+ minExtent: minExtent ?? self.minExtent,
8376+ offset: offset ?? self.offset,
8377+ orient: orient ?? self.orient,
8378+ position: position ?? self.position,
8379+ tickCount: tickCount ?? self.tickCount,
8380+ ticks: ticks ?? self.ticks,
8381+ tickSize: tickSize ?? self.tickSize,
8382+ title: title ?? self.title,
8383+ titleMaxLength: titleMaxLength ?? self.titleMaxLength,
8384+ titlePadding: titlePadding ?? self.titlePadding,
8385+ values: values ?? self.values,
8386+ zindex: zindex ?? self.zindex
8387+ )
8388+ }
8389+
8390+ func jsonData() throws -> Data {
8391+ return try newJSONEncoder().encode(self)
8392+ }
8393+
8394+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8395+ return String(data: try self.jsonData(), encoding: encoding)
8396+ }
8397+}
8398+
8399+enum AxisValue: Codable {
8400+ case dateTime(DateTime)
8401+ case double(Double)
8402+
8403+ init(from decoder: Decoder) throws {
8404+ let container = try decoder.singleValueContainer()
8405+ if let x = try? container.decode(Double.self) {
8406+ self = .double(x)
8407+ return
8408+ }
8409+ if let x = try? container.decode(DateTime.self) {
8410+ self = .dateTime(x)
8411+ return
8412+ }
8413+ throw DecodingError.typeMismatch(AxisValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for AxisValue"))
8414+ }
8415+
8416+ func encode(to encoder: Encoder) throws {
8417+ var container = encoder.singleValueContainer()
8418+ switch self {
8419+ case .dateTime(let x):
8420+ try container.encode(x)
8421+ case .double(let x):
8422+ try container.encode(x)
8423+ }
8424+ }
8425+}
8426+
8427+/// X2 coordinates for ranged `"area"`, `"bar"`, `"rect"`, and `"rule"`.
8428+///
8429+/// Y2 coordinates for ranged `"area"`, `"bar"`, `"rect"`, and `"rule"`.
8430+///
8431+/// Definition object for a data field, its type and transformation of an encoding channel.
8432+///
8433+/// Definition object for a constant value of an encoding channel.
8434+// MARK: - X2Class
8435+struct X2Class: Codable {
8436+ /// Aggregation function for the field
8437+ /// (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
8438+ ///
8439+ /// __Default value:__ `undefined` (None)
8440+ let aggregate: AggregateOp?
8441+ /// A flag for binning a `quantitative` field, or [an object defining binning
8442+ /// parameters](bin.html#params).
8443+ /// If `true`, default [binning parameters](bin.html) will be applied.
8444+ ///
8445+ /// __Default value:__ `false`
8446+ let bin: Bin?
8447+ /// __Required.__ A string defining the name of the field from which to pull a data value
8448+ /// or an object defining iterated values from the [`repeat`](repeat.html) operator.
8449+ ///
8450+ /// __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
8451+ /// (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
8452+ /// If field names contain dots or brackets but are not nested, you can use `\\` to escape
8453+ /// dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
8454+ /// See more details about escaping in the [field documentation](field.html).
8455+ ///
8456+ /// __Note:__ `field` is not required if `aggregate` is `count`.
8457+ let field: Field?
8458+ /// Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
8459+ /// or [a temporal field that gets casted as ordinal](type.html#cast).
8460+ ///
8461+ /// __Default value:__ `undefined` (None)
8462+ let timeUnit: TimeUnit?
8463+ /// The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
8464+ /// `"nominal"`).
8465+ /// It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
8466+ /// [geographic projection](projection.html) is applied.
8467+ let type: ConditionalPredicateValueDefType?
8468+ /// A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
8469+ /// `0` to `1` for opacity).
8470+ let value: ConditionalValueDefValue?
8471+
8472+ enum CodingKeys: String, CodingKey {
8473+ case aggregate = "aggregate"
8474+ case bin = "bin"
8475+ case field = "field"
8476+ case timeUnit = "timeUnit"
8477+ case type = "type"
8478+ case value = "value"
8479+ }
8480+}
8481+
8482+// MARK: X2Class convenience initializers and mutators
8483+
8484+extension X2Class {
8485+ init(data: Data) throws {
8486+ self = try newJSONDecoder().decode(X2Class.self, from: data)
8487+ }
8488+
8489+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
8490+ guard let data = json.data(using: encoding) else {
8491+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
8492+ }
8493+ try self.init(data: data)
8494+ }
8495+
8496+ init(fromURL url: URL) throws {
8497+ try self.init(data: try Data(contentsOf: url))
8498+ }
8499+
8500+ func with(
8501+ aggregate: AggregateOp?? = nil,
8502+ bin: Bin?? = nil,
8503+ field: Field?? = nil,
8504+ timeUnit: TimeUnit?? = nil,
8505+ type: ConditionalPredicateValueDefType?? = nil,
8506+ value: ConditionalValueDefValue?? = nil
8507+ ) -> X2Class {
8508+ return X2Class(
8509+ aggregate: aggregate ?? self.aggregate,
8510+ bin: bin ?? self.bin,
8511+ field: field ?? self.field,
8512+ timeUnit: timeUnit ?? self.timeUnit,
8513+ type: type ?? self.type,
8514+ value: value ?? self.value
8515+ )
8516+ }
8517+
8518+ func jsonData() throws -> Data {
8519+ return try newJSONEncoder().encode(self)
8520+ }
8521+
8522+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8523+ return String(data: try self.jsonData(), encoding: encoding)
8524+ }
8525+}
8526+
8527+/// An object that describes mappings between `row` and `column` channels and their field
8528+/// definitions.
8529+// MARK: - FacetMapping
8530+struct FacetMapping: Codable {
8531+ /// Horizontal facets for trellis plots.
8532+ let column: FacetFieldDef?
8533+ /// Vertical facets for trellis plots.
8534+ let row: FacetFieldDef?
8535+
8536+ enum CodingKeys: String, CodingKey {
8537+ case column = "column"
8538+ case row = "row"
8539+ }
8540+}
8541+
8542+// MARK: FacetMapping convenience initializers and mutators
8543+
8544+extension FacetMapping {
8545+ init(data: Data) throws {
8546+ self = try newJSONDecoder().decode(FacetMapping.self, from: data)
8547+ }
8548+
8549+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
8550+ guard let data = json.data(using: encoding) else {
8551+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
8552+ }
8553+ try self.init(data: data)
8554+ }
8555+
8556+ init(fromURL url: URL) throws {
8557+ try self.init(data: try Data(contentsOf: url))
8558+ }
8559+
8560+ func with(
8561+ column: FacetFieldDef?? = nil,
8562+ row: FacetFieldDef?? = nil
8563+ ) -> FacetMapping {
8564+ return FacetMapping(
8565+ column: column ?? self.column,
8566+ row: row ?? self.row
8567+ )
8568+ }
8569+
8570+ func jsonData() throws -> Data {
8571+ return try newJSONEncoder().encode(self)
8572+ }
8573+
8574+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8575+ return String(data: try self.jsonData(), encoding: encoding)
8576+ }
8577+}
8578+
8579+/// Unit spec that can have a composite mark.
8580+// MARK: - Spec
8581+class Spec: Codable {
8582+ /// An object describing the data source
8583+ let data: DataClass?
8584+ /// Description of this mark for commenting purpose.
8585+ let description: String?
8586+ /// The height of a visualization.
8587+ ///
8588+ /// __Default value:__
8589+ /// - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its y-channel has a
8590+ /// [continuous scale](scale.html#continuous), the height will be the value of
8591+ /// [`config.view.height`](spec.html#config).
8592+ /// - For y-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
8593+ /// value or unspecified, the height is [determined by the range step, paddings, and the
8594+ /// cardinality of the field mapped to y-channel](scale.html#band). Otherwise, if the
8595+ /// `rangeStep` is `null`, the height will be the value of
8596+ /// [`config.view.height`](spec.html#config).
8597+ /// - If no field is mapped to `y` channel, the `height` will be the value of `rangeStep`.
8598+ ///
8599+ /// __Note__: For plots with [`row` and `column` channels](encoding.html#facet), this
8600+ /// represents the height of a single view.
8601+ ///
8602+ /// __See also:__ The documentation for [width and height](size.html) contains more examples.
8603+ let height: Double?
8604+ /// Layer or single view specifications to be layered.
8605+ ///
8606+ /// __Note__: Specifications inside `layer` cannot use `row` and `column` channels as
8607+ /// layering facet specifications is not allowed.
8608+ let layer: [LayerSpec]?
8609+ /// Name of the visualization for later reference.
8610+ let name: String?
8611+ /// Scale, axis, and legend resolutions for layers.
8612+ ///
8613+ /// Scale, axis, and legend resolutions for facets.
8614+ ///
8615+ /// Scale and legend resolutions for repeated charts.
8616+ ///
8617+ /// Scale, axis, and legend resolutions for vertically concatenated charts.
8618+ ///
8619+ /// Scale, axis, and legend resolutions for horizontally concatenated charts.
8620+ let resolve: Resolve?
8621+ /// Title for the plot.
8622+ let title: Title?
8623+ /// An array of data transformations such as filter and new field calculation.
8624+ let transform: [Transform]?
8625+ /// The width of a visualization.
8626+ ///
8627+ /// __Default value:__ This will be determined by the following rules:
8628+ ///
8629+ /// - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its x-channel has a
8630+ /// [continuous scale](scale.html#continuous), the width will be the value of
8631+ /// [`config.view.width`](spec.html#config).
8632+ /// - For x-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
8633+ /// value or unspecified, the width is [determined by the range step, paddings, and the
8634+ /// cardinality of the field mapped to x-channel](scale.html#band). Otherwise, if the
8635+ /// `rangeStep` is `null`, the width will be the value of
8636+ /// [`config.view.width`](spec.html#config).
8637+ /// - If no field is mapped to `x` channel, the `width` will be the value of
8638+ /// [`config.scale.textXRangeStep`](size.html#default-width-and-height) for `text` mark and
8639+ /// the value of `rangeStep` for other marks.
8640+ ///
8641+ /// __Note:__ For plots with [`row` and `column` channels](encoding.html#facet), this
8642+ /// represents the width of a single view.
8643+ ///
8644+ /// __See also:__ The documentation for [width and height](size.html) contains more examples.
8645+ let width: Double?
8646+ /// A key-value mapping between encoding channels and definition of fields.
8647+ let encoding: Encoding?
8648+ /// A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
8649+ /// `"line"`,
8650+ /// * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
8651+ /// object](mark.html#mark-def).
8652+ let mark: AnyMark?
8653+ /// An object defining properties of geographic projection.
8654+ ///
8655+ /// Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
8656+ /// more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
8657+ let projection: Projection?
8658+ /// A key-value mapping between selection names and definitions.
8659+ let selection: [String: SelectionDef]?
8660+ /// An object that describes mappings between `row` and `column` channels and their field
8661+ /// definitions.
8662+ let facet: FacetMapping?
8663+ /// A specification of the view that gets faceted.
8664+ let spec: Spec?
8665+ /// An object that describes what fields should be repeated into views that are laid out as a
8666+ /// `row` or `column`.
8667+ let specRepeat: Repeat?
8668+ /// A list of views that should be concatenated and put into a column.
8669+ let vconcat: [Spec]?
8670+ /// A list of views that should be concatenated and put into a row.
8671+ let hconcat: [Spec]?
8672+
8673+ enum CodingKeys: String, CodingKey {
8674+ case data = "data"
8675+ case description = "description"
8676+ case height = "height"
8677+ case layer = "layer"
8678+ case name = "name"
8679+ case resolve = "resolve"
8680+ case title = "title"
8681+ case transform = "transform"
8682+ case width = "width"
8683+ case encoding = "encoding"
8684+ case mark = "mark"
8685+ case projection = "projection"
8686+ case selection = "selection"
8687+ case facet = "facet"
8688+ case spec = "spec"
8689+ case specRepeat = "repeat"
8690+ case vconcat = "vconcat"
8691+ case hconcat = "hconcat"
8692+ }
8693+
8694+ init(data: DataClass?, description: String?, height: Double?, layer: [LayerSpec]?, name: String?, resolve: Resolve?, title: Title?, transform: [Transform]?, width: Double?, encoding: Encoding?, mark: AnyMark?, projection: Projection?, selection: [String: SelectionDef]?, facet: FacetMapping?, spec: Spec?, specRepeat: Repeat?, vconcat: [Spec]?, hconcat: [Spec]?) {
8695+ self.data = data
8696+ self.description = description
8697+ self.height = height
8698+ self.layer = layer
8699+ self.name = name
8700+ self.resolve = resolve
8701+ self.title = title
8702+ self.transform = transform
8703+ self.width = width
8704+ self.encoding = encoding
8705+ self.mark = mark
8706+ self.projection = projection
8707+ self.selection = selection
8708+ self.facet = facet
8709+ self.spec = spec
8710+ self.specRepeat = specRepeat
8711+ self.vconcat = vconcat
8712+ self.hconcat = hconcat
8713+ }
8714+}
8715+
8716+// MARK: Spec convenience initializers and mutators
8717+
8718+extension Spec {
8719+ convenience init(data: Data) throws {
8720+ let me = try newJSONDecoder().decode(Spec.self, from: data)
8721+ self.init(data: me.data, description: me.description, height: me.height, layer: me.layer, name: me.name, resolve: me.resolve, title: me.title, transform: me.transform, width: me.width, encoding: me.encoding, mark: me.mark, projection: me.projection, selection: me.selection, facet: me.facet, spec: me.spec, specRepeat: me.specRepeat, vconcat: me.vconcat, hconcat: me.hconcat)
8722+ }
8723+
8724+ convenience init(_ json: String, using encoding: String.Encoding = .utf8) throws {
8725+ guard let data = json.data(using: encoding) else {
8726+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
8727+ }
8728+ try self.init(data: data)
8729+ }
8730+
8731+ convenience init(fromURL url: URL) throws {
8732+ try self.init(data: try Data(contentsOf: url))
8733+ }
8734+
8735+ func with(
8736+ data: DataClass?? = nil,
8737+ description: String?? = nil,
8738+ height: Double?? = nil,
8739+ layer: [LayerSpec]?? = nil,
8740+ name: String?? = nil,
8741+ resolve: Resolve?? = nil,
8742+ title: Title?? = nil,
8743+ transform: [Transform]?? = nil,
8744+ width: Double?? = nil,
8745+ encoding: Encoding?? = nil,
8746+ mark: AnyMark?? = nil,
8747+ projection: Projection?? = nil,
8748+ selection: [String: SelectionDef]?? = nil,
8749+ facet: FacetMapping?? = nil,
8750+ spec: Spec?? = nil,
8751+ specRepeat: Repeat?? = nil,
8752+ vconcat: [Spec]?? = nil,
8753+ hconcat: [Spec]?? = nil
8754+ ) -> Spec {
8755+ return Spec(
8756+ data: data ?? self.data,
8757+ description: description ?? self.description,
8758+ height: height ?? self.height,
8759+ layer: layer ?? self.layer,
8760+ name: name ?? self.name,
8761+ resolve: resolve ?? self.resolve,
8762+ title: title ?? self.title,
8763+ transform: transform ?? self.transform,
8764+ width: width ?? self.width,
8765+ encoding: encoding ?? self.encoding,
8766+ mark: mark ?? self.mark,
8767+ projection: projection ?? self.projection,
8768+ selection: selection ?? self.selection,
8769+ facet: facet ?? self.facet,
8770+ spec: spec ?? self.spec,
8771+ specRepeat: specRepeat ?? self.specRepeat,
8772+ vconcat: vconcat ?? self.vconcat,
8773+ hconcat: hconcat ?? self.hconcat
8774+ )
8775+ }
8776+
8777+ func jsonData() throws -> Data {
8778+ return try newJSONEncoder().encode(self)
8779+ }
8780+
8781+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8782+ return String(data: try self.jsonData(), encoding: encoding)
8783+ }
8784+}
8785+
8786+/// A key-value mapping between encoding channels and definition of fields.
8787+// MARK: - Encoding
8788+struct Encoding: Codable {
8789+ /// Color of the marks – either fill or stroke color based on mark type.
8790+ /// By default, `color` represents fill color for `"area"`, `"bar"`, `"tick"`,
8791+ /// `"text"`, `"circle"`, and `"square"` / stroke color for `"line"` and `"point"`.
8792+ ///
8793+ /// __Default value:__ If undefined, the default color depends on [mark
8794+ /// config](config.html#mark)'s `color` property.
8795+ ///
8796+ /// _Note:_ See the scale documentation for more information about customizing [color
8797+ /// scheme](scale.html#scheme).
8798+ let color: MarkPropDefWithCondition?
8799+ /// Additional levels of detail for grouping data in aggregate views and
8800+ /// in line and area marks without mapping data to a specific visual channel.
8801+ let detail: Detail?
8802+ /// A URL to load upon mouse click.
8803+ let href: DefWithCondition?
8804+ /// Opacity of the marks – either can be a value or a range.
8805+ ///
8806+ /// __Default value:__ If undefined, the default opacity depends on [mark
8807+ /// config](config.html#mark)'s `opacity` property.
8808+ let opacity: MarkPropDefWithCondition?
8809+ /// Stack order for stacked marks or order of data points in line marks for connected scatter
8810+ /// plots.
8811+ ///
8812+ /// __Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating
8813+ /// additional aggregation grouping.
8814+ let order: Order?
8815+ /// For `point` marks the supported values are
8816+ /// `"circle"` (default), `"square"`, `"cross"`, `"diamond"`, `"triangle-up"`,
8817+ /// or `"triangle-down"`, or else a custom SVG path string.
8818+ /// For `geoshape` marks it should be a field definition of the geojson data
8819+ ///
8820+ /// __Default value:__ If undefined, the default shape depends on [mark
8821+ /// config](config.html#point-config)'s `shape` property.
8822+ let shape: MarkPropDefWithCondition?
8823+ /// Size of the mark.
8824+ /// - For `"point"`, `"square"` and `"circle"`, – the symbol size, or pixel area of the mark.
8825+ /// - For `"bar"` and `"tick"` – the bar and tick's size.
8826+ /// - For `"text"` – the text's font size.
8827+ /// - Size is currently unsupported for `"line"`, `"area"`, and `"rect"`.
8828+ let size: MarkPropDefWithCondition?
8829+ /// Text of the `text` mark.
8830+ let text: TextDefWithCondition?
8831+ /// The tooltip text to show upon mouse hover.
8832+ let tooltip: TextDefWithCondition?
8833+ /// X coordinates of the marks, or width of horizontal `"bar"` and `"area"`.
8834+ let x: XClass?
8835+ /// X2 coordinates for ranged `"area"`, `"bar"`, `"rect"`, and `"rule"`.
8836+ let x2: X2Class?
8837+ /// Y coordinates of the marks, or height of vertical `"bar"` and `"area"`.
8838+ let y: XClass?
8839+ /// Y2 coordinates for ranged `"area"`, `"bar"`, `"rect"`, and `"rule"`.
8840+ let y2: X2Class?
8841+
8842+ enum CodingKeys: String, CodingKey {
8843+ case color = "color"
8844+ case detail = "detail"
8845+ case href = "href"
8846+ case opacity = "opacity"
8847+ case order = "order"
8848+ case shape = "shape"
8849+ case size = "size"
8850+ case text = "text"
8851+ case tooltip = "tooltip"
8852+ case x = "x"
8853+ case x2 = "x2"
8854+ case y = "y"
8855+ case y2 = "y2"
8856+ }
8857+}
8858+
8859+// MARK: Encoding convenience initializers and mutators
8860+
8861+extension Encoding {
8862+ init(data: Data) throws {
8863+ self = try newJSONDecoder().decode(Encoding.self, from: data)
8864+ }
8865+
8866+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
8867+ guard let data = json.data(using: encoding) else {
8868+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
8869+ }
8870+ try self.init(data: data)
8871+ }
8872+
8873+ init(fromURL url: URL) throws {
8874+ try self.init(data: try Data(contentsOf: url))
8875+ }
8876+
8877+ func with(
8878+ color: MarkPropDefWithCondition?? = nil,
8879+ detail: Detail?? = nil,
8880+ href: DefWithCondition?? = nil,
8881+ opacity: MarkPropDefWithCondition?? = nil,
8882+ order: Order?? = nil,
8883+ shape: MarkPropDefWithCondition?? = nil,
8884+ size: MarkPropDefWithCondition?? = nil,
8885+ text: TextDefWithCondition?? = nil,
8886+ tooltip: TextDefWithCondition?? = nil,
8887+ x: XClass?? = nil,
8888+ x2: X2Class?? = nil,
8889+ y: XClass?? = nil,
8890+ y2: X2Class?? = nil
8891+ ) -> Encoding {
8892+ return Encoding(
8893+ color: color ?? self.color,
8894+ detail: detail ?? self.detail,
8895+ href: href ?? self.href,
8896+ opacity: opacity ?? self.opacity,
8897+ order: order ?? self.order,
8898+ shape: shape ?? self.shape,
8899+ size: size ?? self.size,
8900+ text: text ?? self.text,
8901+ tooltip: tooltip ?? self.tooltip,
8902+ x: x ?? self.x,
8903+ x2: x2 ?? self.x2,
8904+ y: y ?? self.y,
8905+ y2: y2 ?? self.y2
8906+ )
8907+ }
8908+
8909+ func jsonData() throws -> Data {
8910+ return try newJSONEncoder().encode(self)
8911+ }
8912+
8913+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
8914+ return String(data: try self.jsonData(), encoding: encoding)
8915+ }
8916+}
8917+
8918+/// Unit spec that can have a composite mark.
8919+// MARK: - LayerSpec
8920+struct LayerSpec: Codable {
8921+ /// An object describing the data source
8922+ let data: DataClass?
8923+ /// Description of this mark for commenting purpose.
8924+ let description: String?
8925+ /// The height of a visualization.
8926+ ///
8927+ /// __Default value:__
8928+ /// - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its y-channel has a
8929+ /// [continuous scale](scale.html#continuous), the height will be the value of
8930+ /// [`config.view.height`](spec.html#config).
8931+ /// - For y-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
8932+ /// value or unspecified, the height is [determined by the range step, paddings, and the
8933+ /// cardinality of the field mapped to y-channel](scale.html#band). Otherwise, if the
8934+ /// `rangeStep` is `null`, the height will be the value of
8935+ /// [`config.view.height`](spec.html#config).
8936+ /// - If no field is mapped to `y` channel, the `height` will be the value of `rangeStep`.
8937+ ///
8938+ /// __Note__: For plots with [`row` and `column` channels](encoding.html#facet), this
8939+ /// represents the height of a single view.
8940+ ///
8941+ /// __See also:__ The documentation for [width and height](size.html) contains more examples.
8942+ let height: Double?
8943+ /// Layer or single view specifications to be layered.
8944+ ///
8945+ /// __Note__: Specifications inside `layer` cannot use `row` and `column` channels as
8946+ /// layering facet specifications is not allowed.
8947+ let layer: [LayerSpec]?
8948+ /// Name of the visualization for later reference.
8949+ let name: String?
8950+ /// Scale, axis, and legend resolutions for layers.
8951+ let resolve: Resolve?
8952+ /// Title for the plot.
8953+ let title: Title?
8954+ /// An array of data transformations such as filter and new field calculation.
8955+ let transform: [Transform]?
8956+ /// The width of a visualization.
8957+ ///
8958+ /// __Default value:__ This will be determined by the following rules:
8959+ ///
8960+ /// - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its x-channel has a
8961+ /// [continuous scale](scale.html#continuous), the width will be the value of
8962+ /// [`config.view.width`](spec.html#config).
8963+ /// - For x-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
8964+ /// value or unspecified, the width is [determined by the range step, paddings, and the
8965+ /// cardinality of the field mapped to x-channel](scale.html#band). Otherwise, if the
8966+ /// `rangeStep` is `null`, the width will be the value of
8967+ /// [`config.view.width`](spec.html#config).
8968+ /// - If no field is mapped to `x` channel, the `width` will be the value of
8969+ /// [`config.scale.textXRangeStep`](size.html#default-width-and-height) for `text` mark and
8970+ /// the value of `rangeStep` for other marks.
8971+ ///
8972+ /// __Note:__ For plots with [`row` and `column` channels](encoding.html#facet), this
8973+ /// represents the width of a single view.
8974+ ///
8975+ /// __See also:__ The documentation for [width and height](size.html) contains more examples.
8976+ let width: Double?
8977+ /// A key-value mapping between encoding channels and definition of fields.
8978+ let encoding: Encoding?
8979+ /// A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
8980+ /// `"line"`,
8981+ /// * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
8982+ /// object](mark.html#mark-def).
8983+ let mark: AnyMark?
8984+ /// An object defining properties of geographic projection.
8985+ ///
8986+ /// Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
8987+ /// more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
8988+ let projection: Projection?
8989+ /// A key-value mapping between selection names and definitions.
8990+ let selection: [String: SelectionDef]?
8991+
8992+ enum CodingKeys: String, CodingKey {
8993+ case data = "data"
8994+ case description = "description"
8995+ case height = "height"
8996+ case layer = "layer"
8997+ case name = "name"
8998+ case resolve = "resolve"
8999+ case title = "title"
9000+ case transform = "transform"
9001+ case width = "width"
9002+ case encoding = "encoding"
9003+ case mark = "mark"
9004+ case projection = "projection"
9005+ case selection = "selection"
9006+ }
9007+}
9008+
9009+// MARK: LayerSpec convenience initializers and mutators
9010+
9011+extension LayerSpec {
9012+ init(data: Data) throws {
9013+ self = try newJSONDecoder().decode(LayerSpec.self, from: data)
9014+ }
9015+
9016+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9017+ guard let data = json.data(using: encoding) else {
9018+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9019+ }
9020+ try self.init(data: data)
9021+ }
9022+
9023+ init(fromURL url: URL) throws {
9024+ try self.init(data: try Data(contentsOf: url))
9025+ }
9026+
9027+ func with(
9028+ data: DataClass?? = nil,
9029+ description: String?? = nil,
9030+ height: Double?? = nil,
9031+ layer: [LayerSpec]?? = nil,
9032+ name: String?? = nil,
9033+ resolve: Resolve?? = nil,
9034+ title: Title?? = nil,
9035+ transform: [Transform]?? = nil,
9036+ width: Double?? = nil,
9037+ encoding: Encoding?? = nil,
9038+ mark: AnyMark?? = nil,
9039+ projection: Projection?? = nil,
9040+ selection: [String: SelectionDef]?? = nil
9041+ ) -> LayerSpec {
9042+ return LayerSpec(
9043+ data: data ?? self.data,
9044+ description: description ?? self.description,
9045+ height: height ?? self.height,
9046+ layer: layer ?? self.layer,
9047+ name: name ?? self.name,
9048+ resolve: resolve ?? self.resolve,
9049+ title: title ?? self.title,
9050+ transform: transform ?? self.transform,
9051+ width: width ?? self.width,
9052+ encoding: encoding ?? self.encoding,
9053+ mark: mark ?? self.mark,
9054+ projection: projection ?? self.projection,
9055+ selection: selection ?? self.selection
9056+ )
9057+ }
9058+
9059+ func jsonData() throws -> Data {
9060+ return try newJSONEncoder().encode(self)
9061+ }
9062+
9063+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9064+ return String(data: try self.jsonData(), encoding: encoding)
9065+ }
9066+}
9067+
9068+/// A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
9069+/// `"line"`,
9070+/// * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
9071+/// object](mark.html#mark-def).
9072+enum AnyMark: Codable {
9073+ case enumeration(Mark)
9074+ case markDef(MarkDef)
9075+
9076+ init(from decoder: Decoder) throws {
9077+ let container = try decoder.singleValueContainer()
9078+ if let x = try? container.decode(Mark.self) {
9079+ self = .enumeration(x)
9080+ return
9081+ }
9082+ if let x = try? container.decode(MarkDef.self) {
9083+ self = .markDef(x)
9084+ return
9085+ }
9086+ throw DecodingError.typeMismatch(AnyMark.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for AnyMark"))
9087+ }
9088+
9089+ func encode(to encoder: Encoder) throws {
9090+ var container = encoder.singleValueContainer()
9091+ switch self {
9092+ case .enumeration(let x):
9093+ try container.encode(x)
9094+ case .markDef(let x):
9095+ try container.encode(x)
9096+ }
9097+ }
9098+}
9099+
9100+// MARK: - MarkDef
9101+struct MarkDef: Codable {
9102+ /// The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
9103+ let align: HorizontalAlign?
9104+ /// The rotation angle of the text, in degrees.
9105+ let angle: Double?
9106+ /// The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
9107+ ///
9108+ /// __Default value:__ `"middle"`
9109+ let baseline: VerticalAlign?
9110+ /// Whether a mark be clipped to the enclosing group’s width and height.
9111+ let clip: Bool?
9112+ /// Default color. Note that `fill` and `stroke` have higher precedence than `color` and
9113+ /// will override `color`.
9114+ ///
9115+ /// __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
9116+ ///
9117+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
9118+ let color: String?
9119+ /// The mouse cursor used over the mark. Any valid [CSS cursor
9120+ /// type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
9121+ let cursor: Cursor?
9122+ /// The horizontal offset, in pixels, between the text label and its anchor point. The offset
9123+ /// is applied after rotation by the _angle_ property.
9124+ let dx: Double?
9125+ /// The vertical offset, in pixels, between the text label and its anchor point. The offset
9126+ /// is applied after rotation by the _angle_ property.
9127+ let dy: Double?
9128+ /// Default Fill Color. This has higher precedence than config.color
9129+ ///
9130+ /// __Default value:__ (None)
9131+ let fill: String?
9132+ /// Whether the mark's color should be used as fill color instead of stroke color.
9133+ ///
9134+ /// __Default value:__ `true` for all marks except `point` and `false` for `point`.
9135+ ///
9136+ /// __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
9137+ ///
9138+ /// __Note:__ This property cannot be used in a [style config](mark.html#style-config).
9139+ let filled: Bool?
9140+ /// The fill opacity (value between [0,1]).
9141+ ///
9142+ /// __Default value:__ `1`
9143+ let fillOpacity: Double?
9144+ /// The typeface to set the text in (e.g., `"Helvetica Neue"`).
9145+ let font: String?
9146+ /// The font size, in pixels.
9147+ let fontSize: Double?
9148+ /// The font style (e.g., `"italic"`).
9149+ let fontStyle: FontStyle?
9150+ /// The font weight (e.g., `"bold"`).
9151+ let fontWeight: FontWeightUnion?
9152+ /// A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
9153+ let href: String?
9154+ /// The line interpolation method to use for line and area marks. One of the following:
9155+ /// - `"linear"`: piecewise linear segments, as in a polyline.
9156+ /// - `"linear-closed"`: close the linear segments to form a polygon.
9157+ /// - `"step"`: alternate between horizontal and vertical segments, as in a step function.
9158+ /// - `"step-before"`: alternate between vertical and horizontal segments, as in a step
9159+ /// function.
9160+ /// - `"step-after"`: alternate between horizontal and vertical segments, as in a step
9161+ /// function.
9162+ /// - `"basis"`: a B-spline, with control point duplication on the ends.
9163+ /// - `"basis-open"`: an open B-spline; may not intersect the start or end.
9164+ /// - `"basis-closed"`: a closed B-spline, as in a loop.
9165+ /// - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
9166+ /// - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
9167+ /// will intersect other control points.
9168+ /// - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
9169+ /// - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
9170+ /// spline.
9171+ /// - `"monotone"`: cubic interpolation that preserves monotonicity in y.
9172+ let interpolate: Interpolate?
9173+ /// The maximum length of the text mark in pixels (default 0, indicating no limit). The text
9174+ /// value will be automatically truncated if the rendered size exceeds the limit.
9175+ let limit: Double?
9176+ /// The overall opacity (value between [0,1]).
9177+ ///
9178+ /// __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
9179+ /// `square` marks or layered `bar` charts and `1` otherwise.
9180+ let opacity: Double?
9181+ /// The orientation of a non-stacked bar, tick, area, and line charts.
9182+ /// The value is either horizontal (default) or vertical.
9183+ /// - For bar, rule and tick, this determines whether the size of the bar and tick
9184+ /// should be applied to x or y dimension.
9185+ /// - For area, this property determines the orient property of the Vega output.
9186+ /// - For line, this property determines the sort order of the points in the line
9187+ /// if `config.sortLineBy` is not specified.
9188+ /// For stacked charts, this is always determined by the orientation of the stack;
9189+ /// therefore explicitly specified value will be ignored.
9190+ let orient: Orient?
9191+ /// Polar coordinate radial offset, in pixels, of the text label from the origin determined
9192+ /// by the `x` and `y` properties.
9193+ let radius: Double?
9194+ /// The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
9195+ /// `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
9196+ ///
9197+ /// __Default value:__ `"circle"`
9198+ let shape: String?
9199+ /// The pixel area each the point/circle/square.
9200+ /// For example: in the case of circles, the radius is determined in part by the square root
9201+ /// of the size value.
9202+ ///
9203+ /// __Default value:__ `30`
9204+ let size: Double?
9205+ /// Default Stroke Color. This has higher precedence than config.color
9206+ ///
9207+ /// __Default value:__ (None)
9208+ let stroke: String?
9209+ /// An array of alternating stroke, space lengths for creating dashed or dotted lines.
9210+ let strokeDash: [Double]?
9211+ /// The offset (in pixels) into which to begin drawing with the stroke dash array.
9212+ let strokeDashOffset: Double?
9213+ /// The stroke opacity (value between [0,1]).
9214+ ///
9215+ /// __Default value:__ `1`
9216+ let strokeOpacity: Double?
9217+ /// The stroke width, in pixels.
9218+ let strokeWidth: Double?
9219+ /// A string or array of strings indicating the name of custom styles to apply to the mark. A
9220+ /// style is a named collection of mark property defaults defined within the [style
9221+ /// configuration](mark.html#style-config). If style is an array, later styles will override
9222+ /// earlier styles. Any [mark properties](encoding.html#mark-prop) explicitly defined within
9223+ /// the `encoding` will override a style default.
9224+ ///
9225+ /// __Default value:__ The mark's name. For example, a bar mark will have style `"bar"` by
9226+ /// default.
9227+ /// __Note:__ Any specified style will augment the default style. For example, a bar mark
9228+ /// with `"style": "foo"` will receive from `config.style.bar` and `config.style.foo` (the
9229+ /// specified style `"foo"` has higher precedence).
9230+ let style: Style?
9231+ /// Depending on the interpolation type, sets the tension parameter (for line and area marks).
9232+ let tension: Double?
9233+ /// Placeholder text if the `text` channel is not specified
9234+ let text: String?
9235+ /// Polar coordinate angle, in radians, of the text label from the origin determined by the
9236+ /// `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
9237+ /// `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
9238+ /// indicating "north".
9239+ let theta: Double?
9240+ /// The mark type.
9241+ /// One of `"bar"`, `"circle"`, `"square"`, `"tick"`, `"line"`,
9242+ /// `"area"`, `"point"`, `"geoshape"`, `"rule"`, and `"text"`.
9243+ let type: Mark
9244+
9245+ enum CodingKeys: String, CodingKey {
9246+ case align = "align"
9247+ case angle = "angle"
9248+ case baseline = "baseline"
9249+ case clip = "clip"
9250+ case color = "color"
9251+ case cursor = "cursor"
9252+ case dx = "dx"
9253+ case dy = "dy"
9254+ case fill = "fill"
9255+ case filled = "filled"
9256+ case fillOpacity = "fillOpacity"
9257+ case font = "font"
9258+ case fontSize = "fontSize"
9259+ case fontStyle = "fontStyle"
9260+ case fontWeight = "fontWeight"
9261+ case href = "href"
9262+ case interpolate = "interpolate"
9263+ case limit = "limit"
9264+ case opacity = "opacity"
9265+ case orient = "orient"
9266+ case radius = "radius"
9267+ case shape = "shape"
9268+ case size = "size"
9269+ case stroke = "stroke"
9270+ case strokeDash = "strokeDash"
9271+ case strokeDashOffset = "strokeDashOffset"
9272+ case strokeOpacity = "strokeOpacity"
9273+ case strokeWidth = "strokeWidth"
9274+ case style = "style"
9275+ case tension = "tension"
9276+ case text = "text"
9277+ case theta = "theta"
9278+ case type = "type"
9279+ }
9280+}
9281+
9282+// MARK: MarkDef convenience initializers and mutators
9283+
9284+extension MarkDef {
9285+ init(data: Data) throws {
9286+ self = try newJSONDecoder().decode(MarkDef.self, from: data)
9287+ }
9288+
9289+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9290+ guard let data = json.data(using: encoding) else {
9291+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9292+ }
9293+ try self.init(data: data)
9294+ }
9295+
9296+ init(fromURL url: URL) throws {
9297+ try self.init(data: try Data(contentsOf: url))
9298+ }
9299+
9300+ func with(
9301+ align: HorizontalAlign?? = nil,
9302+ angle: Double?? = nil,
9303+ baseline: VerticalAlign?? = nil,
9304+ clip: Bool?? = nil,
9305+ color: String?? = nil,
9306+ cursor: Cursor?? = nil,
9307+ dx: Double?? = nil,
9308+ dy: Double?? = nil,
9309+ fill: String?? = nil,
9310+ filled: Bool?? = nil,
9311+ fillOpacity: Double?? = nil,
9312+ font: String?? = nil,
9313+ fontSize: Double?? = nil,
9314+ fontStyle: FontStyle?? = nil,
9315+ fontWeight: FontWeightUnion?? = nil,
9316+ href: String?? = nil,
9317+ interpolate: Interpolate?? = nil,
9318+ limit: Double?? = nil,
9319+ opacity: Double?? = nil,
9320+ orient: Orient?? = nil,
9321+ radius: Double?? = nil,
9322+ shape: String?? = nil,
9323+ size: Double?? = nil,
9324+ stroke: String?? = nil,
9325+ strokeDash: [Double]?? = nil,
9326+ strokeDashOffset: Double?? = nil,
9327+ strokeOpacity: Double?? = nil,
9328+ strokeWidth: Double?? = nil,
9329+ style: Style?? = nil,
9330+ tension: Double?? = nil,
9331+ text: String?? = nil,
9332+ theta: Double?? = nil,
9333+ type: Mark? = nil
9334+ ) -> MarkDef {
9335+ return MarkDef(
9336+ align: align ?? self.align,
9337+ angle: angle ?? self.angle,
9338+ baseline: baseline ?? self.baseline,
9339+ clip: clip ?? self.clip,
9340+ color: color ?? self.color,
9341+ cursor: cursor ?? self.cursor,
9342+ dx: dx ?? self.dx,
9343+ dy: dy ?? self.dy,
9344+ fill: fill ?? self.fill,
9345+ filled: filled ?? self.filled,
9346+ fillOpacity: fillOpacity ?? self.fillOpacity,
9347+ font: font ?? self.font,
9348+ fontSize: fontSize ?? self.fontSize,
9349+ fontStyle: fontStyle ?? self.fontStyle,
9350+ fontWeight: fontWeight ?? self.fontWeight,
9351+ href: href ?? self.href,
9352+ interpolate: interpolate ?? self.interpolate,
9353+ limit: limit ?? self.limit,
9354+ opacity: opacity ?? self.opacity,
9355+ orient: orient ?? self.orient,
9356+ radius: radius ?? self.radius,
9357+ shape: shape ?? self.shape,
9358+ size: size ?? self.size,
9359+ stroke: stroke ?? self.stroke,
9360+ strokeDash: strokeDash ?? self.strokeDash,
9361+ strokeDashOffset: strokeDashOffset ?? self.strokeDashOffset,
9362+ strokeOpacity: strokeOpacity ?? self.strokeOpacity,
9363+ strokeWidth: strokeWidth ?? self.strokeWidth,
9364+ style: style ?? self.style,
9365+ tension: tension ?? self.tension,
9366+ text: text ?? self.text,
9367+ theta: theta ?? self.theta,
9368+ type: type ?? self.type
9369+ )
9370+ }
9371+
9372+ func jsonData() throws -> Data {
9373+ return try newJSONEncoder().encode(self)
9374+ }
9375+
9376+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9377+ return String(data: try self.jsonData(), encoding: encoding)
9378+ }
9379+}
9380+
9381+/// A string or array of strings indicating the name of custom styles to apply to the mark. A
9382+/// style is a named collection of mark property defaults defined within the [style
9383+/// configuration](mark.html#style-config). If style is an array, later styles will override
9384+/// earlier styles. Any [mark properties](encoding.html#mark-prop) explicitly defined within
9385+/// the `encoding` will override a style default.
9386+///
9387+/// __Default value:__ The mark's name. For example, a bar mark will have style `"bar"` by
9388+/// default.
9389+/// __Note:__ Any specified style will augment the default style. For example, a bar mark
9390+/// with `"style": "foo"` will receive from `config.style.bar` and `config.style.foo` (the
9391+/// specified style `"foo"` has higher precedence).
9392+///
9393+/// A [mark style property](config.html#style) to apply to the title text mark.
9394+///
9395+/// __Default value:__ `"group-title"`.
9396+enum Style: Codable {
9397+ case string(String)
9398+ case stringArray([String])
9399+
9400+ init(from decoder: Decoder) throws {
9401+ let container = try decoder.singleValueContainer()
9402+ if let x = try? container.decode([String].self) {
9403+ self = .stringArray(x)
9404+ return
9405+ }
9406+ if let x = try? container.decode(String.self) {
9407+ self = .string(x)
9408+ return
9409+ }
9410+ throw DecodingError.typeMismatch(Style.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Style"))
9411+ }
9412+
9413+ func encode(to encoder: Encoder) throws {
9414+ var container = encoder.singleValueContainer()
9415+ switch self {
9416+ case .string(let x):
9417+ try container.encode(x)
9418+ case .stringArray(let x):
9419+ try container.encode(x)
9420+ }
9421+ }
9422+}
9423+
9424+/// All types of primitive marks.
9425+///
9426+/// The mark type.
9427+/// One of `"bar"`, `"circle"`, `"square"`, `"tick"`, `"line"`,
9428+/// `"area"`, `"point"`, `"geoshape"`, `"rule"`, and `"text"`.
9429+enum Mark: String, Codable {
9430+ case area = "area"
9431+ case bar = "bar"
9432+ case line = "line"
9433+ case point = "point"
9434+ case text = "text"
9435+ case tick = "tick"
9436+ case rect = "rect"
9437+ case rule = "rule"
9438+ case circle = "circle"
9439+ case square = "square"
9440+ case geoshape = "geoshape"
9441+}
9442+
9443+/// An object defining properties of geographic projection.
9444+///
9445+/// Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
9446+/// more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
9447+// MARK: - Projection
9448+struct Projection: Codable {
9449+ /// Sets the projection’s center to the specified center, a two-element array of longitude
9450+ /// and latitude in degrees.
9451+ ///
9452+ /// __Default value:__ `[0, 0]`
9453+ let center: [Double]?
9454+ /// Sets the projection’s clipping circle radius to the specified angle in degrees. If
9455+ /// `null`, switches to [antimeridian](http://bl.ocks.org/mbostock/3788999) cutting rather
9456+ /// than small-circle clipping.
9457+ let clipAngle: Double?
9458+ /// Sets the projection’s viewport clip extent to the specified bounds in pixels. The extent
9459+ /// bounds are specified as an array `[[x0, y0], [x1, y1]]`, where `x0` is the left-side of
9460+ /// the viewport, `y0` is the top, `x1` is the right and `y1` is the bottom. If `null`, no
9461+ /// viewport clipping is performed.
9462+ let clipExtent: [[Double]]?
9463+ let coefficient: Double?
9464+ let distance: Double?
9465+ let fraction: Double?
9466+ let lobes: Double?
9467+ let parallel: Double?
9468+ /// Sets the threshold for the projection’s [adaptive
9469+ /// resampling](http://bl.ocks.org/mbostock/3795544) to the specified value in pixels. This
9470+ /// value corresponds to the [Douglas–Peucker
9471+ /// distance](http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm).
9472+ /// If precision is not specified, returns the projection’s current resampling precision
9473+ /// which defaults to `√0.5 ≅ 0.70710…`.
9474+ let precision: [String: TitleFontWeight]?
9475+ let radius: Double?
9476+ let ratio: Double?
9477+ /// Sets the projection’s three-axis rotation to the specified angles, which must be a two-
9478+ /// or three-element array of numbers [`lambda`, `phi`, `gamma`] specifying the rotation
9479+ /// angles in degrees about each spherical axis. (These correspond to yaw, pitch and roll.)
9480+ ///
9481+ /// __Default value:__ `[0, 0, 0]`
9482+ let rotate: [Double]?
9483+ let spacing: Double?
9484+ let tilt: Double?
9485+ /// The cartographic projection to use. This value is case-insensitive, for example
9486+ /// `"albers"` and `"Albers"` indicate the same projection type. You can find all valid
9487+ /// projection types [in the
9488+ /// documentation](https://vega.github.io/vega-lite/docs/projection.html#projection-types).
9489+ ///
9490+ /// __Default value:__ `mercator`
9491+ let type: VGProjectionType?
9492+
9493+ enum CodingKeys: String, CodingKey {
9494+ case center = "center"
9495+ case clipAngle = "clipAngle"
9496+ case clipExtent = "clipExtent"
9497+ case coefficient = "coefficient"
9498+ case distance = "distance"
9499+ case fraction = "fraction"
9500+ case lobes = "lobes"
9501+ case parallel = "parallel"
9502+ case precision = "precision"
9503+ case radius = "radius"
9504+ case ratio = "ratio"
9505+ case rotate = "rotate"
9506+ case spacing = "spacing"
9507+ case tilt = "tilt"
9508+ case type = "type"
9509+ }
9510+}
9511+
9512+// MARK: Projection convenience initializers and mutators
9513+
9514+extension Projection {
9515+ init(data: Data) throws {
9516+ self = try newJSONDecoder().decode(Projection.self, from: data)
9517+ }
9518+
9519+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9520+ guard let data = json.data(using: encoding) else {
9521+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9522+ }
9523+ try self.init(data: data)
9524+ }
9525+
9526+ init(fromURL url: URL) throws {
9527+ try self.init(data: try Data(contentsOf: url))
9528+ }
9529+
9530+ func with(
9531+ center: [Double]?? = nil,
9532+ clipAngle: Double?? = nil,
9533+ clipExtent: [[Double]]?? = nil,
9534+ coefficient: Double?? = nil,
9535+ distance: Double?? = nil,
9536+ fraction: Double?? = nil,
9537+ lobes: Double?? = nil,
9538+ parallel: Double?? = nil,
9539+ precision: [String: TitleFontWeight]?? = nil,
9540+ radius: Double?? = nil,
9541+ ratio: Double?? = nil,
9542+ rotate: [Double]?? = nil,
9543+ spacing: Double?? = nil,
9544+ tilt: Double?? = nil,
9545+ type: VGProjectionType?? = nil
9546+ ) -> Projection {
9547+ return Projection(
9548+ center: center ?? self.center,
9549+ clipAngle: clipAngle ?? self.clipAngle,
9550+ clipExtent: clipExtent ?? self.clipExtent,
9551+ coefficient: coefficient ?? self.coefficient,
9552+ distance: distance ?? self.distance,
9553+ fraction: fraction ?? self.fraction,
9554+ lobes: lobes ?? self.lobes,
9555+ parallel: parallel ?? self.parallel,
9556+ precision: precision ?? self.precision,
9557+ radius: radius ?? self.radius,
9558+ ratio: ratio ?? self.ratio,
9559+ rotate: rotate ?? self.rotate,
9560+ spacing: spacing ?? self.spacing,
9561+ tilt: tilt ?? self.tilt,
9562+ type: type ?? self.type
9563+ )
9564+ }
9565+
9566+ func jsonData() throws -> Data {
9567+ return try newJSONEncoder().encode(self)
9568+ }
9569+
9570+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9571+ return String(data: try self.jsonData(), encoding: encoding)
9572+ }
9573+}
9574+
9575+/// Scale, axis, and legend resolutions for layers.
9576+///
9577+/// Defines how scales, axes, and legends from different specs should be combined. Resolve is
9578+/// a mapping from `scale`, `axis`, and `legend` to a mapping from channels to resolutions.
9579+///
9580+/// Scale, axis, and legend resolutions for facets.
9581+///
9582+/// Scale and legend resolutions for repeated charts.
9583+///
9584+/// Scale, axis, and legend resolutions for vertically concatenated charts.
9585+///
9586+/// Scale, axis, and legend resolutions for horizontally concatenated charts.
9587+// MARK: - Resolve
9588+struct Resolve: Codable {
9589+ let axis: AxisResolveMap?
9590+ let legend: LegendResolveMap?
9591+ let scale: ScaleResolveMap?
9592+
9593+ enum CodingKeys: String, CodingKey {
9594+ case axis = "axis"
9595+ case legend = "legend"
9596+ case scale = "scale"
9597+ }
9598+}
9599+
9600+// MARK: Resolve convenience initializers and mutators
9601+
9602+extension Resolve {
9603+ init(data: Data) throws {
9604+ self = try newJSONDecoder().decode(Resolve.self, from: data)
9605+ }
9606+
9607+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9608+ guard let data = json.data(using: encoding) else {
9609+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9610+ }
9611+ try self.init(data: data)
9612+ }
9613+
9614+ init(fromURL url: URL) throws {
9615+ try self.init(data: try Data(contentsOf: url))
9616+ }
9617+
9618+ func with(
9619+ axis: AxisResolveMap?? = nil,
9620+ legend: LegendResolveMap?? = nil,
9621+ scale: ScaleResolveMap?? = nil
9622+ ) -> Resolve {
9623+ return Resolve(
9624+ axis: axis ?? self.axis,
9625+ legend: legend ?? self.legend,
9626+ scale: scale ?? self.scale
9627+ )
9628+ }
9629+
9630+ func jsonData() throws -> Data {
9631+ return try newJSONEncoder().encode(self)
9632+ }
9633+
9634+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9635+ return String(data: try self.jsonData(), encoding: encoding)
9636+ }
9637+}
9638+
9639+// MARK: - AxisResolveMap
9640+struct AxisResolveMap: Codable {
9641+ let x: ResolveMode?
9642+ let y: ResolveMode?
9643+
9644+ enum CodingKeys: String, CodingKey {
9645+ case x = "x"
9646+ case y = "y"
9647+ }
9648+}
9649+
9650+// MARK: AxisResolveMap convenience initializers and mutators
9651+
9652+extension AxisResolveMap {
9653+ init(data: Data) throws {
9654+ self = try newJSONDecoder().decode(AxisResolveMap.self, from: data)
9655+ }
9656+
9657+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9658+ guard let data = json.data(using: encoding) else {
9659+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9660+ }
9661+ try self.init(data: data)
9662+ }
9663+
9664+ init(fromURL url: URL) throws {
9665+ try self.init(data: try Data(contentsOf: url))
9666+ }
9667+
9668+ func with(
9669+ x: ResolveMode?? = nil,
9670+ y: ResolveMode?? = nil
9671+ ) -> AxisResolveMap {
9672+ return AxisResolveMap(
9673+ x: x ?? self.x,
9674+ y: y ?? self.y
9675+ )
9676+ }
9677+
9678+ func jsonData() throws -> Data {
9679+ return try newJSONEncoder().encode(self)
9680+ }
9681+
9682+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9683+ return String(data: try self.jsonData(), encoding: encoding)
9684+ }
9685+}
9686+
9687+enum ResolveMode: String, Codable {
9688+ case independent = "independent"
9689+ case shared = "shared"
9690+}
9691+
9692+// MARK: - LegendResolveMap
9693+struct LegendResolveMap: Codable {
9694+ let color: ResolveMode?
9695+ let opacity: ResolveMode?
9696+ let shape: ResolveMode?
9697+ let size: ResolveMode?
9698+
9699+ enum CodingKeys: String, CodingKey {
9700+ case color = "color"
9701+ case opacity = "opacity"
9702+ case shape = "shape"
9703+ case size = "size"
9704+ }
9705+}
9706+
9707+// MARK: LegendResolveMap convenience initializers and mutators
9708+
9709+extension LegendResolveMap {
9710+ init(data: Data) throws {
9711+ self = try newJSONDecoder().decode(LegendResolveMap.self, from: data)
9712+ }
9713+
9714+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9715+ guard let data = json.data(using: encoding) else {
9716+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9717+ }
9718+ try self.init(data: data)
9719+ }
9720+
9721+ init(fromURL url: URL) throws {
9722+ try self.init(data: try Data(contentsOf: url))
9723+ }
9724+
9725+ func with(
9726+ color: ResolveMode?? = nil,
9727+ opacity: ResolveMode?? = nil,
9728+ shape: ResolveMode?? = nil,
9729+ size: ResolveMode?? = nil
9730+ ) -> LegendResolveMap {
9731+ return LegendResolveMap(
9732+ color: color ?? self.color,
9733+ opacity: opacity ?? self.opacity,
9734+ shape: shape ?? self.shape,
9735+ size: size ?? self.size
9736+ )
9737+ }
9738+
9739+ func jsonData() throws -> Data {
9740+ return try newJSONEncoder().encode(self)
9741+ }
9742+
9743+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9744+ return String(data: try self.jsonData(), encoding: encoding)
9745+ }
9746+}
9747+
9748+// MARK: - ScaleResolveMap
9749+struct ScaleResolveMap: Codable {
9750+ let color: ResolveMode?
9751+ let opacity: ResolveMode?
9752+ let shape: ResolveMode?
9753+ let size: ResolveMode?
9754+ let x: ResolveMode?
9755+ let y: ResolveMode?
9756+
9757+ enum CodingKeys: String, CodingKey {
9758+ case color = "color"
9759+ case opacity = "opacity"
9760+ case shape = "shape"
9761+ case size = "size"
9762+ case x = "x"
9763+ case y = "y"
9764+ }
9765+}
9766+
9767+// MARK: ScaleResolveMap convenience initializers and mutators
9768+
9769+extension ScaleResolveMap {
9770+ init(data: Data) throws {
9771+ self = try newJSONDecoder().decode(ScaleResolveMap.self, from: data)
9772+ }
9773+
9774+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9775+ guard let data = json.data(using: encoding) else {
9776+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9777+ }
9778+ try self.init(data: data)
9779+ }
9780+
9781+ init(fromURL url: URL) throws {
9782+ try self.init(data: try Data(contentsOf: url))
9783+ }
9784+
9785+ func with(
9786+ color: ResolveMode?? = nil,
9787+ opacity: ResolveMode?? = nil,
9788+ shape: ResolveMode?? = nil,
9789+ size: ResolveMode?? = nil,
9790+ x: ResolveMode?? = nil,
9791+ y: ResolveMode?? = nil
9792+ ) -> ScaleResolveMap {
9793+ return ScaleResolveMap(
9794+ color: color ?? self.color,
9795+ opacity: opacity ?? self.opacity,
9796+ shape: shape ?? self.shape,
9797+ size: size ?? self.size,
9798+ x: x ?? self.x,
9799+ y: y ?? self.y
9800+ )
9801+ }
9802+
9803+ func jsonData() throws -> Data {
9804+ return try newJSONEncoder().encode(self)
9805+ }
9806+
9807+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9808+ return String(data: try self.jsonData(), encoding: encoding)
9809+ }
9810+}
9811+
9812+// MARK: - SelectionDef
9813+struct SelectionDef: Codable {
9814+ /// Establish a two-way binding between a single selection and input elements
9815+ /// (also known as dynamic query widgets). A binding takes the form of
9816+ /// Vega's [input element binding definition](https://vega.github.io/vega/docs/signals/#bind)
9817+ /// or can be a mapping between projected field/encodings and binding definitions.
9818+ ///
9819+ /// See the [bind transform](bind.html) documentation for more information.
9820+ ///
9821+ /// Establishes a two-way binding between the interval selection and the scales
9822+ /// used within the same view. This allows a user to interactively pan and
9823+ /// zoom the view.
9824+ let bind: BindUnion?
9825+ /// By default, all data values are considered to lie within an empty selection.
9826+ /// When set to `none`, empty selections contain no data values.
9827+ let empty: Empty?
9828+ /// An array of encoding channels. The corresponding data field values
9829+ /// must match for a data tuple to fall within the selection.
9830+ let encodings: [SingleDefChannel]?
9831+ /// An array of field names whose values must match for a data tuple to
9832+ /// fall within the selection.
9833+ let fields: [String]?
9834+ /// When true, an invisible voronoi diagram is computed to accelerate discrete
9835+ /// selection. The data value _nearest_ the mouse cursor is added to the selection.
9836+ ///
9837+ /// See the [nearest transform](nearest.html) documentation for more information.
9838+ let nearest: Bool?
9839+ /// A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
9840+ /// selector) that triggers the selection.
9841+ /// For interval selections, the event stream must specify a [start and
9842+ /// end](https://vega.github.io/vega/docs/event-streams/#between-filters).
9843+ let on: JSONAny?
9844+ /// With layered and multi-view displays, a strategy that determines how
9845+ /// selections' data queries are resolved when applied in a filter transform,
9846+ /// conditional encoding rule, or scale domain.
9847+ let resolve: SelectionResolution?
9848+ let type: SelectionDefType
9849+ /// Controls whether data values should be toggled or only ever inserted into
9850+ /// multi selections. Can be `true`, `false` (for insertion only), or a
9851+ /// [Vega expression](https://vega.github.io/vega/docs/expressions/).
9852+ ///
9853+ /// __Default value:__ `true`, which corresponds to `event.shiftKey` (i.e.,
9854+ /// data values are toggled when a user interacts with the shift-key pressed).
9855+ ///
9856+ /// See the [toggle transform](toggle.html) documentation for more information.
9857+ let toggle: Translate?
9858+ /// An interval selection also adds a rectangle mark to depict the
9859+ /// extents of the interval. The `mark` property can be used to customize the
9860+ /// appearance of the mark.
9861+ let mark: BrushConfig?
9862+ /// When truthy, allows a user to interactively move an interval selection
9863+ /// back-and-forth. Can be `true`, `false` (to disable panning), or a
9864+ /// [Vega event stream definition](https://vega.github.io/vega/docs/event-streams/)
9865+ /// which must include a start and end event to trigger continuous panning.
9866+ ///
9867+ /// __Default value:__ `true`, which corresponds to
9868+ /// `[mousedown, window:mouseup] > window:mousemove!` which corresponds to
9869+ /// clicks and dragging within an interval selection to reposition it.
9870+ let translate: Translate?
9871+ /// When truthy, allows a user to interactively resize an interval selection.
9872+ /// Can be `true`, `false` (to disable zooming), or a [Vega event stream
9873+ /// definition](https://vega.github.io/vega/docs/event-streams/). Currently,
9874+ /// only `wheel` events are supported.
9875+ ///
9876+ ///
9877+ /// __Default value:__ `true`, which corresponds to `wheel!`.
9878+ let zoom: Translate?
9879+
9880+ enum CodingKeys: String, CodingKey {
9881+ case bind = "bind"
9882+ case empty = "empty"
9883+ case encodings = "encodings"
9884+ case fields = "fields"
9885+ case nearest = "nearest"
9886+ case on = "on"
9887+ case resolve = "resolve"
9888+ case type = "type"
9889+ case toggle = "toggle"
9890+ case mark = "mark"
9891+ case translate = "translate"
9892+ case zoom = "zoom"
9893+ }
9894+}
9895+
9896+// MARK: SelectionDef convenience initializers and mutators
9897+
9898+extension SelectionDef {
9899+ init(data: Data) throws {
9900+ self = try newJSONDecoder().decode(SelectionDef.self, from: data)
9901+ }
9902+
9903+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
9904+ guard let data = json.data(using: encoding) else {
9905+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
9906+ }
9907+ try self.init(data: data)
9908+ }
9909+
9910+ init(fromURL url: URL) throws {
9911+ try self.init(data: try Data(contentsOf: url))
9912+ }
9913+
9914+ func with(
9915+ bind: BindUnion?? = nil,
9916+ empty: Empty?? = nil,
9917+ encodings: [SingleDefChannel]?? = nil,
9918+ fields: [String]?? = nil,
9919+ nearest: Bool?? = nil,
9920+ on: JSONAny?? = nil,
9921+ resolve: SelectionResolution?? = nil,
9922+ type: SelectionDefType? = nil,
9923+ toggle: Translate?? = nil,
9924+ mark: BrushConfig?? = nil,
9925+ translate: Translate?? = nil,
9926+ zoom: Translate?? = nil
9927+ ) -> SelectionDef {
9928+ return SelectionDef(
9929+ bind: bind ?? self.bind,
9930+ empty: empty ?? self.empty,
9931+ encodings: encodings ?? self.encodings,
9932+ fields: fields ?? self.fields,
9933+ nearest: nearest ?? self.nearest,
9934+ on: on ?? self.on,
9935+ resolve: resolve ?? self.resolve,
9936+ type: type ?? self.type,
9937+ toggle: toggle ?? self.toggle,
9938+ mark: mark ?? self.mark,
9939+ translate: translate ?? self.translate,
9940+ zoom: zoom ?? self.zoom
9941+ )
9942+ }
9943+
9944+ func jsonData() throws -> Data {
9945+ return try newJSONEncoder().encode(self)
9946+ }
9947+
9948+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
9949+ return String(data: try self.jsonData(), encoding: encoding)
9950+ }
9951+}
9952+
9953+enum BindUnion: Codable {
9954+ case enumeration(BindEnum)
9955+ case vgBindingMap([String: VGBinding])
9956+
9957+ init(from decoder: Decoder) throws {
9958+ let container = try decoder.singleValueContainer()
9959+ if let x = try? container.decode(BindEnum.self) {
9960+ self = .enumeration(x)
9961+ return
9962+ }
9963+ if let x = try? container.decode([String: VGBinding].self) {
9964+ self = .vgBindingMap(x)
9965+ return
9966+ }
9967+ throw DecodingError.typeMismatch(BindUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for BindUnion"))
9968+ }
9969+
9970+ func encode(to encoder: Encoder) throws {
9971+ var container = encoder.singleValueContainer()
9972+ switch self {
9973+ case .enumeration(let x):
9974+ try container.encode(x)
9975+ case .vgBindingMap(let x):
9976+ try container.encode(x)
9977+ }
9978+ }
9979+}
9980+
9981+enum SelectionDefType: String, Codable {
9982+ case single = "single"
9983+ case multi = "multi"
9984+ case interval = "interval"
9985+}
9986+
9987+enum Title: Codable {
9988+ case string(String)
9989+ case titleParams(TitleParams)
9990+
9991+ init(from decoder: Decoder) throws {
9992+ let container = try decoder.singleValueContainer()
9993+ if let x = try? container.decode(String.self) {
9994+ self = .string(x)
9995+ return
9996+ }
9997+ if let x = try? container.decode(TitleParams.self) {
9998+ self = .titleParams(x)
9999+ return
10000+ }
10001+ throw DecodingError.typeMismatch(Title.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Title"))
10002+ }
10003+
10004+ func encode(to encoder: Encoder) throws {
10005+ var container = encoder.singleValueContainer()
10006+ switch self {
10007+ case .string(let x):
10008+ try container.encode(x)
10009+ case .titleParams(let x):
10010+ try container.encode(x)
10011+ }
10012+ }
10013+}
10014+
10015+// MARK: - TitleParams
10016+struct TitleParams: Codable {
10017+ /// The anchor position for placing the title. One of `"start"`, `"middle"`, or `"end"`. For
10018+ /// example, with an orientation of top these anchor positions map to a left-, center-, or
10019+ /// right-aligned title.
10020+ ///
10021+ /// __Default value:__ `"middle"` for [single](spec.html) and [layered](layer.html) views.
10022+ /// `"start"` for other composite views.
10023+ ///
10024+ /// __Note:__ [For now](https://github.com/vega/vega-lite/issues/2875), `anchor` is only
10025+ /// customizable only for [single](spec.html) and [layered](layer.html) views. For other
10026+ /// composite views, `anchor` is always `"start"`.
10027+ let anchor: Anchor?
10028+ /// The orthogonal offset in pixels by which to displace the title from its position along
10029+ /// the edge of the chart.
10030+ let offset: Double?
10031+ /// The orientation of the title relative to the chart. One of `"top"` (the default),
10032+ /// `"bottom"`, `"left"`, or `"right"`.
10033+ let orient: TitleOrient?
10034+ /// A [mark style property](config.html#style) to apply to the title text mark.
10035+ ///
10036+ /// __Default value:__ `"group-title"`.
10037+ let style: Style?
10038+ /// The title text.
10039+ let text: String
10040+
10041+ enum CodingKeys: String, CodingKey {
10042+ case anchor = "anchor"
10043+ case offset = "offset"
10044+ case orient = "orient"
10045+ case style = "style"
10046+ case text = "text"
10047+ }
10048+}
10049+
10050+// MARK: TitleParams convenience initializers and mutators
10051+
10052+extension TitleParams {
10053+ init(data: Data) throws {
10054+ self = try newJSONDecoder().decode(TitleParams.self, from: data)
10055+ }
10056+
10057+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
10058+ guard let data = json.data(using: encoding) else {
10059+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
10060+ }
10061+ try self.init(data: data)
10062+ }
10063+
10064+ init(fromURL url: URL) throws {
10065+ try self.init(data: try Data(contentsOf: url))
10066+ }
10067+
10068+ func with(
10069+ anchor: Anchor?? = nil,
10070+ offset: Double?? = nil,
10071+ orient: TitleOrient?? = nil,
10072+ style: Style?? = nil,
10073+ text: String? = nil
10074+ ) -> TitleParams {
10075+ return TitleParams(
10076+ anchor: anchor ?? self.anchor,
10077+ offset: offset ?? self.offset,
10078+ orient: orient ?? self.orient,
10079+ style: style ?? self.style,
10080+ text: text ?? self.text
10081+ )
10082+ }
10083+
10084+ func jsonData() throws -> Data {
10085+ return try newJSONEncoder().encode(self)
10086+ }
10087+
10088+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
10089+ return String(data: try self.jsonData(), encoding: encoding)
10090+ }
10091+}
10092+
10093+// MARK: - Transform
10094+struct Transform: Codable {
10095+ /// The `filter` property must be one of the predicate definitions:
10096+ /// (1) an [expression](types.html#expression) string,
10097+ /// where `datum` can be used to refer to the current data object;
10098+ /// (2) one of the field predicates: [equal predicate](filter.html#equal-predicate);
10099+ /// [range predicate](filter.html#range-predicate), [one-of
10100+ /// predicate](filter.html#one-of-predicate);
10101+ /// (3) a [selection predicate](filter.html#selection-predicate);
10102+ /// or (4) a logical operand that combines (1), (2), or (3).
10103+ let filter: LogicalOperandPredicate?
10104+ /// The field for storing the computed formula value.
10105+ ///
10106+ /// The field or fields for storing the computed formula value.
10107+ /// If `from.fields` is specified, the transform will use the same names for `as`.
10108+ /// If `from.fields` is not specified, `as` has to be a string and we put the whole object
10109+ /// into the data under the specified name.
10110+ ///
10111+ /// The output fields at which to write the start and end bin values.
10112+ ///
10113+ /// The output field to write the timeUnit value.
10114+ let transformAs: Style?
10115+ /// A [expression](types.html#expression) string. Use the variable `datum` to refer to the
10116+ /// current data object.
10117+ let calculate: String?
10118+ /// The default value to use if lookup fails.
10119+ ///
10120+ /// __Default value:__ `null`
10121+ let transformDefault: String?
10122+ /// Secondary data reference.
10123+ let from: LookupData?
10124+ /// Key in primary data source.
10125+ let lookup: String?
10126+ /// An object indicating bin properties, or simply `true` for using default bin parameters.
10127+ let bin: Bin?
10128+ /// The data field to bin.
10129+ ///
10130+ /// The data field to apply time unit.
10131+ let field: String?
10132+ /// The timeUnit.
10133+ let timeUnit: TimeUnit?
10134+ /// Array of objects that define fields to aggregate.
10135+ let aggregate: [AggregatedFieldDef]?
10136+ /// The data fields to group by. If not specified, a single group containing all data objects
10137+ /// will be used.
10138+ let groupby: [String]?
10139+
10140+ enum CodingKeys: String, CodingKey {
10141+ case filter = "filter"
10142+ case transformAs = "as"
10143+ case calculate = "calculate"
10144+ case transformDefault = "default"
10145+ case from = "from"
10146+ case lookup = "lookup"
10147+ case bin = "bin"
10148+ case field = "field"
10149+ case timeUnit = "timeUnit"
10150+ case aggregate = "aggregate"
10151+ case groupby = "groupby"
10152+ }
10153+}
10154+
10155+// MARK: Transform convenience initializers and mutators
10156+
10157+extension Transform {
10158+ init(data: Data) throws {
10159+ self = try newJSONDecoder().decode(Transform.self, from: data)
10160+ }
10161+
10162+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
10163+ guard let data = json.data(using: encoding) else {
10164+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
10165+ }
10166+ try self.init(data: data)
10167+ }
10168+
10169+ init(fromURL url: URL) throws {
10170+ try self.init(data: try Data(contentsOf: url))
10171+ }
10172+
10173+ func with(
10174+ filter: LogicalOperandPredicate?? = nil,
10175+ transformAs: Style?? = nil,
10176+ calculate: String?? = nil,
10177+ transformDefault: String?? = nil,
10178+ from: LookupData?? = nil,
10179+ lookup: String?? = nil,
10180+ bin: Bin?? = nil,
10181+ field: String?? = nil,
10182+ timeUnit: TimeUnit?? = nil,
10183+ aggregate: [AggregatedFieldDef]?? = nil,
10184+ groupby: [String]?? = nil
10185+ ) -> Transform {
10186+ return Transform(
10187+ filter: filter ?? self.filter,
10188+ transformAs: transformAs ?? self.transformAs,
10189+ calculate: calculate ?? self.calculate,
10190+ transformDefault: transformDefault ?? self.transformDefault,
10191+ from: from ?? self.from,
10192+ lookup: lookup ?? self.lookup,
10193+ bin: bin ?? self.bin,
10194+ field: field ?? self.field,
10195+ timeUnit: timeUnit ?? self.timeUnit,
10196+ aggregate: aggregate ?? self.aggregate,
10197+ groupby: groupby ?? self.groupby
10198+ )
10199+ }
10200+
10201+ func jsonData() throws -> Data {
10202+ return try newJSONEncoder().encode(self)
10203+ }
10204+
10205+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
10206+ return String(data: try self.jsonData(), encoding: encoding)
10207+ }
10208+}
10209+
10210+// MARK: - AggregatedFieldDef
10211+struct AggregatedFieldDef: Codable {
10212+ /// The output field names to use for each aggregated field.
10213+ let aggregatedFieldDefAs: String
10214+ /// The data field for which to compute aggregate function.
10215+ let field: String
10216+ /// The aggregation operations to apply to the fields, such as sum, average or count.
10217+ /// See the [full list of supported aggregation
10218+ /// operations](https://vega.github.io/vega-lite/docs/aggregate.html#ops)
10219+ /// for more information.
10220+ let op: AggregateOp
10221+
10222+ enum CodingKeys: String, CodingKey {
10223+ case aggregatedFieldDefAs = "as"
10224+ case field = "field"
10225+ case op = "op"
10226+ }
10227+}
10228+
10229+// MARK: AggregatedFieldDef convenience initializers and mutators
10230+
10231+extension AggregatedFieldDef {
10232+ init(data: Data) throws {
10233+ self = try newJSONDecoder().decode(AggregatedFieldDef.self, from: data)
10234+ }
10235+
10236+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
10237+ guard let data = json.data(using: encoding) else {
10238+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
10239+ }
10240+ try self.init(data: data)
10241+ }
10242+
10243+ init(fromURL url: URL) throws {
10244+ try self.init(data: try Data(contentsOf: url))
10245+ }
10246+
10247+ func with(
10248+ aggregatedFieldDefAs: String? = nil,
10249+ field: String? = nil,
10250+ op: AggregateOp? = nil
10251+ ) -> AggregatedFieldDef {
10252+ return AggregatedFieldDef(
10253+ aggregatedFieldDefAs: aggregatedFieldDefAs ?? self.aggregatedFieldDefAs,
10254+ field: field ?? self.field,
10255+ op: op ?? self.op
10256+ )
10257+ }
10258+
10259+ func jsonData() throws -> Data {
10260+ return try newJSONEncoder().encode(self)
10261+ }
10262+
10263+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
10264+ return String(data: try self.jsonData(), encoding: encoding)
10265+ }
10266+}
10267+
10268+/// Secondary data reference.
10269+// MARK: - LookupData
10270+struct LookupData: Codable {
10271+ /// Secondary data source to lookup in.
10272+ let data: DataClass
10273+ /// Fields in foreign data to lookup.
10274+ /// If not specified, the entire object is queried.
10275+ let fields: [String]?
10276+ /// Key in data to lookup.
10277+ let key: String
10278+
10279+ enum CodingKeys: String, CodingKey {
10280+ case data = "data"
10281+ case fields = "fields"
10282+ case key = "key"
10283+ }
10284+}
10285+
10286+// MARK: LookupData convenience initializers and mutators
10287+
10288+extension LookupData {
10289+ init(data: Data) throws {
10290+ self = try newJSONDecoder().decode(LookupData.self, from: data)
10291+ }
10292+
10293+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
10294+ guard let data = json.data(using: encoding) else {
10295+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
10296+ }
10297+ try self.init(data: data)
10298+ }
10299+
10300+ init(fromURL url: URL) throws {
10301+ try self.init(data: try Data(contentsOf: url))
10302+ }
10303+
10304+ func with(
10305+ data: DataClass? = nil,
10306+ fields: [String]?? = nil,
10307+ key: String? = nil
10308+ ) -> LookupData {
10309+ return LookupData(
10310+ data: data ?? self.data,
10311+ fields: fields ?? self.fields,
10312+ key: key ?? self.key
10313+ )
10314+ }
10315+
10316+ func jsonData() throws -> Data {
10317+ return try newJSONEncoder().encode(self)
10318+ }
10319+
10320+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
10321+ return String(data: try self.jsonData(), encoding: encoding)
10322+ }
10323+}
10324+
10325+/// An object that describes what fields should be repeated into views that are laid out as a
10326+/// `row` or `column`.
10327+// MARK: - Repeat
10328+struct Repeat: Codable {
10329+ /// Horizontal repeated views.
10330+ let column: [String]?
10331+ /// Vertical repeated views.
10332+ let row: [String]?
10333+
10334+ enum CodingKeys: String, CodingKey {
10335+ case column = "column"
10336+ case row = "row"
10337+ }
10338+}
10339+
10340+// MARK: Repeat convenience initializers and mutators
10341+
10342+extension Repeat {
10343+ init(data: Data) throws {
10344+ self = try newJSONDecoder().decode(Repeat.self, from: data)
10345+ }
10346+
10347+ init(_ json: String, using encoding: String.Encoding = .utf8) throws {
10348+ guard let data = json.data(using: encoding) else {
10349+ throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
10350+ }
10351+ try self.init(data: data)
10352+ }
10353+
10354+ init(fromURL url: URL) throws {
10355+ try self.init(data: try Data(contentsOf: url))
10356+ }
10357+
10358+ func with(
10359+ column: [String]?? = nil,
10360+ row: [String]?? = nil
10361+ ) -> Repeat {
10362+ return Repeat(
10363+ column: column ?? self.column,
10364+ row: row ?? self.row
10365+ )
10366+ }
10367+
10368+ func jsonData() throws -> Data {
10369+ return try newJSONEncoder().encode(self)
10370+ }
10371+
10372+ func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
10373+ return String(data: try self.jsonData(), encoding: encoding)
10374+ }
10375+}
10376+
10377+// MARK: - Helper functions for creating encoders and decoders
10378+
10379+func newJSONDecoder() -> JSONDecoder {
10380+ let decoder = JSONDecoder()
10381+ decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
10382+ let container = try decoder.singleValueContainer()
10383+ let dateStr = try container.decode(String.self)
10384+
10385+ let formatter = DateFormatter()
10386+ formatter.calendar = Calendar(identifier: .iso8601)
10387+ formatter.locale = Locale(identifier: "en_US_POSIX")
10388+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
10389+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
10390+ if let date = formatter.date(from: dateStr) {
10391+ return date
10392+ }
10393+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
10394+ if let date = formatter.date(from: dateStr) {
10395+ return date
10396+ }
10397+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
10398+ })
10399+ return decoder
10400+}
10401+
10402+func newJSONEncoder() -> JSONEncoder {
10403+ let encoder = JSONEncoder()
10404+ let formatter = DateFormatter()
10405+ formatter.calendar = Calendar(identifier: .iso8601)
10406+ formatter.locale = Locale(identifier: "en_US_POSIX")
10407+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
10408+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
10409+ encoder.dateEncodingStrategy = .formatted(formatter)
10410+ return encoder
10411+}
10412+
10413+// MARK: - Encode/decode helpers
10414+
10415+class JSONNull: Codable, Hashable {
10416+
10417+ public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
10418+ return true
10419+ }
10420+
10421+ public func hash(into hasher: inout Hasher) {
10422+ hasher.combine(0)
10423+ }
10424+
10425+ public init() {}
10426+
10427+ public required init(from decoder: Decoder) throws {
10428+ let container = try decoder.singleValueContainer()
10429+ if !container.decodeNil() {
10430+ throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
10431+ }
10432+ }
10433+
10434+ public func encode(to encoder: Encoder) throws {
10435+ var container = encoder.singleValueContainer()
10436+ try container.encodeNil()
10437+ }
10438+}
10439+
10440+class JSONCodingKey: CodingKey {
10441+ let key: String
10442+
10443+ required init?(intValue: Int) {
10444+ return nil
10445+ }
10446+
10447+ required init?(stringValue: String) {
10448+ key = stringValue
10449+ }
10450+
10451+ var intValue: Int? {
10452+ return nil
10453+ }
10454+
10455+ var stringValue: String {
10456+ return key
10457+ }
10458+}
10459+
10460+class JSONAny: Codable {
10461+
10462+ let value: Any
10463+
10464+ static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
10465+ let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
10466+ return DecodingError.typeMismatch(JSONAny.self, context)
10467+ }
10468+
10469+ static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
10470+ let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
10471+ return EncodingError.invalidValue(value, context)
10472+ }
10473+
10474+ static func decode(from container: SingleValueDecodingContainer) throws -> Any {
10475+ if let value = try? container.decode(Bool.self) {
10476+ return value
10477+ }
10478+ if let value = try? container.decode(Int64.self) {
10479+ return value
10480+ }
10481+ if let value = try? container.decode(Double.self) {
10482+ return value
10483+ }
10484+ if let value = try? container.decode(String.self) {
10485+ return value
10486+ }
10487+ if container.decodeNil() {
10488+ return JSONNull()
10489+ }
10490+ throw decodingError(forCodingPath: container.codingPath)
10491+ }
10492+
10493+ static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
10494+ if let value = try? container.decode(Bool.self) {
10495+ return value
10496+ }
10497+ if let value = try? container.decode(Int64.self) {
10498+ return value
10499+ }
10500+ if let value = try? container.decode(Double.self) {
10501+ return value
10502+ }
10503+ if let value = try? container.decode(String.self) {
10504+ return value
10505+ }
10506+ if let value = try? container.decodeNil() {
10507+ if value {
10508+ return JSONNull()
10509+ }
10510+ }
10511+ if var container = try? container.nestedUnkeyedContainer() {
10512+ return try decodeArray(from: &container)
10513+ }
10514+ if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
10515+ return try decodeDictionary(from: &container)
10516+ }
10517+ throw decodingError(forCodingPath: container.codingPath)
10518+ }
10519+
10520+ static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
10521+ if let value = try? container.decode(Bool.self, forKey: key) {
10522+ return value
10523+ }
10524+ if let value = try? container.decode(Int64.self, forKey: key) {
10525+ return value
10526+ }
10527+ if let value = try? container.decode(Double.self, forKey: key) {
10528+ return value
10529+ }
10530+ if let value = try? container.decode(String.self, forKey: key) {
10531+ return value
10532+ }
10533+ if let value = try? container.decodeNil(forKey: key) {
10534+ if value {
10535+ return JSONNull()
10536+ }
10537+ }
10538+ if var container = try? container.nestedUnkeyedContainer(forKey: key) {
10539+ return try decodeArray(from: &container)
10540+ }
10541+ if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
10542+ return try decodeDictionary(from: &container)
10543+ }
10544+ throw decodingError(forCodingPath: container.codingPath)
10545+ }
10546+
10547+ static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
10548+ var arr: [Any] = []
10549+ while !container.isAtEnd {
10550+ let value = try decode(from: &container)
10551+ arr.append(value)
10552+ }
10553+ return arr
10554+ }
10555+
10556+ static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
10557+ var dict = [String: Any]()
10558+ for key in container.allKeys {
10559+ let value = try decode(from: &container, forKey: key)
10560+ dict[key.stringValue] = value
10561+ }
10562+ return dict
10563+ }
10564+
10565+ static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
10566+ for value in array {
10567+ if let value = value as? Bool {
10568+ try container.encode(value)
10569+ } else if let value = value as? Int64 {
10570+ try container.encode(value)
10571+ } else if let value = value as? Double {
10572+ try container.encode(value)
10573+ } else if let value = value as? String {
10574+ try container.encode(value)
10575+ } else if value is JSONNull {
10576+ try container.encodeNil()
10577+ } else if let value = value as? [Any] {
10578+ var container = container.nestedUnkeyedContainer()
10579+ try encode(to: &container, array: value)
10580+ } else if let value = value as? [String: Any] {
10581+ var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
10582+ try encode(to: &container, dictionary: value)
10583+ } else {
10584+ throw encodingError(forValue: value, codingPath: container.codingPath)
10585+ }
10586+ }
10587+ }
10588+
10589+ static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
10590+ for (key, value) in dictionary {
10591+ let key = JSONCodingKey(stringValue: key)!
10592+ if let value = value as? Bool {
10593+ try container.encode(value, forKey: key)
10594+ } else if let value = value as? Int64 {
10595+ try container.encode(value, forKey: key)
10596+ } else if let value = value as? Double {
10597+ try container.encode(value, forKey: key)
10598+ } else if let value = value as? String {
10599+ try container.encode(value, forKey: key)
10600+ } else if value is JSONNull {
10601+ try container.encodeNil(forKey: key)
10602+ } else if let value = value as? [Any] {
10603+ var container = container.nestedUnkeyedContainer(forKey: key)
10604+ try encode(to: &container, array: value)
10605+ } else if let value = value as? [String: Any] {
10606+ var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
10607+ try encode(to: &container, dictionary: value)
10608+ } else {
10609+ throw encodingError(forValue: value, codingPath: container.codingPath)
10610+ }
10611+ }
10612+ }
10613+
10614+ static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
10615+ if let value = value as? Bool {
10616+ try container.encode(value)
10617+ } else if let value = value as? Int64 {
10618+ try container.encode(value)
10619+ } else if let value = value as? Double {
10620+ try container.encode(value)
10621+ } else if let value = value as? String {
10622+ try container.encode(value)
10623+ } else if value is JSONNull {
10624+ try container.encodeNil()
10625+ } else {
10626+ throw encodingError(forValue: value, codingPath: container.codingPath)
10627+ }
10628+ }
10629+
10630+ public required init(from decoder: Decoder) throws {
10631+ if var arrayContainer = try? decoder.unkeyedContainer() {
10632+ self.value = try JSONAny.decodeArray(from: &arrayContainer)
10633+ } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
10634+ self.value = try JSONAny.decodeDictionary(from: &container)
10635+ } else {
10636+ let container = try decoder.singleValueContainer()
10637+ self.value = try JSONAny.decode(from: container)
10638+ }
10639+ }
10640+
10641+ public func encode(to encoder: Encoder) throws {
10642+ if let arr = self.value as? [Any] {
10643+ var container = encoder.unkeyedContainer()
10644+ try JSONAny.encode(to: &container, array: arr)
10645+ } else if let dict = self.value as? [String: Any] {
10646+ var container = encoder.container(keyedBy: JSONCodingKey.self)
10647+ try JSONAny.encode(to: &container, dictionary: dict)
10648+ } else {
10649+ var container = encoder.singleValueContainer()
10650+ try JSONAny.encode(to: &container, value: self.value)
10651+ }
10652+ }
10653+}
No generated files match these filters.