Compare commits

...

260 Commits

Author SHA1 Message Date
Daniel García Aubert
85881d99dd Release 0.15.1-cdb5 2018-11-20 17:47:20 +01:00
Daniel G. Aubert
d3a5c97143
Merge pull request #50 from CartoDB/nodejs-10
Drop support for oldest Node.js versions and support latest LTS releases
2018-11-20 17:45:16 +01:00
Daniel García Aubert
8ed3ad15c1 Add CHANGELOG.carto.md 2018-11-20 17:36:46 +01:00
Daniel García Aubert
72f79efbdd Prepare next release version 2018-11-20 17:36:30 +01:00
Daniel García Aubert
161516b721 Support package-lock.json 2018-10-29 12:47:17 +01:00
Daniel García Aubert
e48d5ff993 Drop support for oldest Node.js versions and support latest LTS releases 2018-10-29 12:37:47 +01:00
IagoLast
31abb8bee0 0.15.1 2018-05-10 16:07:00 +02:00
IagoLast
e2177cf443 Add release scripts 2018-05-10 16:06:56 +02:00
Carlos Matallín
664b45cdde
Merge pull request #47 from CartoDB/shield_placement_type
adds shield-placement-type to torque-reference
2018-02-23 11:05:33 +01:00
Alegoiko
368dad0d11 adds shield-placement-type to torque-reference 2018-02-20 12:01:45 +01:00
David M
4dfe5361b3 Merge pull request #45 from CartoDB/ampersand-fix
Fix for ampersand on values (JS only)
2017-10-20 15:08:27 +02:00
David Manzanares
3596991362 Fix for ampersand on values (JS only) 2017-10-20 12:45:03 +02:00
David M
bf4c67034a Merge pull request #44 from CartoDB/js-ampersand-fix
Fix ampersand escaping on JS values
2017-10-10 18:20:57 +02:00
David Manzanares
ad5d919139 Use regular expression to replace all occurrences 2017-10-10 18:12:01 +02:00
David Manzanares
9fb894181d Fix replacement 2017-10-10 18:10:40 +02:00
David Manzanares
ffdf2d3c9b Fix ampersand escaping on JS values 2017-10-10 18:00:47 +02:00
David M
cbe66020f9 Merge pull request #42 from CartoDB/dasharrayJSsupport
Cover 'numbers' type, like in line-dasharray
2017-09-01 17:33:15 +02:00
David Manzanares
056081ace6 Cover 'numbers' type, like in line-dasharray 2017-09-01 13:30:19 +02:00
Pablo Alonso
a48ba58cae Merge pull request #41 from CartoDB/carto-40
Ignore zoom for the filtered field when the value is the default one.
2017-08-23 13:26:00 +02:00
Pablo Alonso Garcia
9f67f1bdde Dist files 2017-08-23 13:25:35 +02:00
IagoLast
55c0786130 Use tree.Zoom.all instead custom constant 2017-08-21 17:40:04 +02:00
Pablo Alonso Garcia
fef8cb369f Minor copy changes 2017-08-21 17:35:08 +02:00
IagoLast
d35e54859a Ignore zoom for the filtered field when the value is the default one. 2017-08-21 16:33:05 +02:00
IagoLast
daafa9fbf2 Merge pull request #39 from CartoDB/expose-filtered-field
Expose filtered field
2017-08-18 16:46:13 +02:00
IagoLast
34799db0cc Boy scout rule 2017-08-18 15:59:04 +02:00
IagoLast
41133a65ad Make test compatible with an old node version 2017-08-18 13:21:22 +02:00
IagoLast
1016f6870c Refactor filtered field code 2017-08-18 13:10:15 +02:00
IagoLast
72f93abf16 Expose filtered field 2017-08-18 12:58:01 +02:00
IagoLast
a3538d9007 Add filtered field tests 2017-08-18 12:57:45 +02:00
IagoLast
66cc146a02 Merge pull request #38 from CartoDB/use-hexa-instead-octal
Use hexadecimal values instead octal
2017-08-01 11:04:01 +02:00
IagoLast
aab27e6be8 Use hexadecimal values instead octal
Strict mode in ECMAScript 5 forbids octal syntax. This octal literals
are breaking the webpack compilation in upper packages like tangram-cartocss.

Mapbox have already [fixed this](https://github.com/mapbox/carto/blob/master/lib/carto/index.js#L104)

This PR changes the octal literals and uses hexadecimal ones.
2017-07-31 18:02:13 +02:00
IagoLast
cea1e7c620 Merge pull request #37 from CartoDB/36-remove-console-log
Remove console.log from tests
2017-07-31 17:58:14 +02:00
IagoLast
f8a9995050 Add a tdd option into the package.json scripts
This option includes a env variable to control the verbosity of the test logs.
2017-07-28 12:01:37 +02:00
IagoLast
0de8b82ff9 Remove console.log from tests 2017-07-26 13:08:05 +02:00
Francisco López
7d00bfdf23 Merge pull request #35 from CartoDB/v-release
Bump version
2017-06-15 15:30:33 +02:00
Francisco López
3db2fa322b Bump version 2017-06-15 14:54:29 +02:00
Francisco López
f4a2605a23 Release dist files 2017-05-19 13:57:16 +02:00
Francisco López
d95967247d Merge branch 'master' of github.com:CartoDB/carto 2017-05-19 13:56:36 +02:00
Francisco López
da8707a17f Release changes! 2017-05-19 13:56:26 +02:00
Francisco López
3f9f6ef40d Merge pull request #27 from CartoDB/reference-errors
[WIP] Reference errors (attempt 2)
2017-05-19 13:55:39 +02:00
Francisco López
b051ae284e Remove throw check 2017-05-19 13:51:27 +02:00
Francisco López
ea880ccf7b Check throw 2017-05-19 12:24:50 +02:00
Francisco López
ab412165b2 Set correct indentation 2017-05-19 12:21:00 +02:00
Francisco López
7974fb0a05 Remove editor.test.js 2017-05-19 12:20:22 +02:00
Francisco López
59c0208caa Fix tests.
Now we don't run the parser so there is no errors, we can't look for errors.
2017-04-17 16:00:07 +02:00
Francisco López
9b5fb6a408 Make the error flag more strict 2017-04-17 15:48:55 +02:00
Daniel García Aubert
945f5efb74 Release 0.15.1-cdb3 2017-03-07 11:10:29 +01:00
Daniel
decdcc5d46 Merge pull request #30 from CartoDB/support-multiple-values
Support multiple values/operands
2017-03-07 11:03:23 +01:00
Daniel García Aubert
34bd0a045d Merge branch 'master' into support-multiple-values 2017-03-07 10:46:59 +01:00
Daniel
ed819c3b51 Merge pull request #29 from CartoDB/24-reference-error
Support `=~` operator
2017-03-07 10:43:04 +01:00
Daniel García Aubert
a42afef5a8 Add assertions 2017-03-06 19:20:01 +01:00
Daniel García Aubert
410ecfd3c7 Use constructor for test validation 2017-03-06 12:04:29 +01:00
Daniel García Aubert
8c75b4b0c6 Remove spaces 2017-03-06 11:59:41 +01:00
Daniel García Aubert
fd94fbd2e6 Support numbers for '=~' operator 2017-03-06 11:57:07 +01:00
Daniel García Aubert
5c4bed9593 Keep spaces 2017-03-06 11:38:59 +01:00
Raul Ochoa
2c092f6b39 Support multiple values/operands 2017-03-05 23:31:32 +01:00
Daniel García Aubert
cc2104eb49 Fix typo 2017-03-03 15:35:57 +01:00
Daniel García Aubert
c780998dc8 Prevent TypeError when parsing '=~' operator 2017-03-03 15:27:42 +01:00
Daniel García Aubert
0063ddba7f Support '=~' operator 2017-03-03 14:36:14 +01:00
Francisco López
510847a3b2 Make the validation in the correct place 2017-02-03 16:49:30 +01:00
Raul Ochoa
0c1990f655 Merge branch 'master' into reference-errors 2017-02-01 12:25:57 +01:00
Raul Ochoa
7315428079 Adds test to validate supported features are validated and work 2017-02-01 12:15:46 +01:00
Raul Ochoa
515fbd0991 Only set previous reference if it existed 2017-02-01 11:23:39 +01:00
Raul Ochoa
975abe9b5c Set tree data just before render happens 2017-02-01 11:16:03 +01:00
Raul Ochoa
b6186d884c Merge pull request #26 from CartoDB/revert-21-reference-errors
Revert "Throw an error when not valid ccss"
2017-02-01 11:12:22 +01:00
Raul Ochoa
e6ba32bc07 Revert "Throw an error when not valid ccss" 2017-02-01 10:44:42 +01:00
Raul Ochoa
fd4caf7595 Merge pull request #21 from CartoDB/reference-errors
Throw an error when not valid ccss
2017-01-31 15:36:38 +01:00
Raul Ochoa
45e59f6a5b Revert changes not needed for PR 2017-01-31 15:23:18 +01:00
Raul Ochoa
dfecbbb976 Revert changes in rendering_js test 2017-01-31 15:22:13 +01:00
Raul Ochoa
50fa97564e Strict parser mode.
When options.strict is set to true, it throws parse errors associated to invalid rules,
or in general rules not compliant with the given reference.
2017-01-31 15:19:47 +01:00
Raul Ochoa
8f9982c313 Fix reference to make test pass due to valid error message 2017-01-31 14:50:56 +01:00
Raul Ochoa
01c6f0c6e5 Validate the expected error message, make test to fail 2017-01-31 14:50:09 +01:00
Raul Ochoa
7e02aac641 Reference options from a valid object 2017-01-31 11:31:11 +01:00
María Checa
4f792fbead Merge pull request #25 from CartoDB/allow_expression_in_polygon_pattern_file
Added expression property to `polygon-pattern-file` and `line-pattern-file`
2017-01-19 18:18:05 +01:00
María Checa
9e12a3b0d8 Added expression property to line-pattern-file 2017-01-19 16:44:49 +01:00
María Checa
09d6384b1f Added expression property to polygon-pattern-file 2017-01-19 16:39:10 +01:00
Raul Ochoa
11ffba0a8e Isolate reference as options test 2017-01-17 17:34:28 +01:00
Francisco López
199d41f20d Throw an error when not valid ccss 2017-01-11 11:39:33 +01:00
Raul Ochoa
e931f91475 Merge pull request #20 from CartoDB/more-expressions
More expressions
2017-01-04 17:32:21 +01:00
Raul Ochoa
11d597e733 Accept expressions in marker file 2017-01-03 19:03:39 +01:00
Raul Ochoa
1960aca276 Accept expresions in polygon fill 2017-01-03 19:03:10 +01:00
Raul Ochoa
f17aea8657 Merge pull request #18 from CartoDB/turbo-carto-fn-support
Adds support for other turbo-carto functions
2016-07-20 14:32:34 +02:00
Raul Ochoa
dfaed546e0 Remove only from test 2016-07-20 13:19:00 +02:00
Raul Ochoa
803f0c0a49 Adds support for other turbo-carto functions 2016-07-20 13:17:10 +02:00
Raul Ochoa
0d6f9d4634 Ignore idea based configurations 2016-07-20 13:08:41 +02:00
javi santana
c042733845 Merge pull request #17 from CartoDB/update_reference
adds expression: true for attributes supported by turbo
2016-06-01 14:37:37 +02:00
javi
1fc486b1b9 adds expression: true for attributes supported by turbo 2016-06-01 14:31:14 +02:00
Buti
b50ee48386 Merge pull request #16 from CartoDB/turbo
adds parser support for arrays and also updates reference to add "ram…
2016-05-31 15:12:06 +02:00
javi
cf5886579f adds parser support for arrays and also updates reference to add "ramp" function 2016-05-31 12:40:31 +02:00
Buti
72d005a082 Merge pull request #15 from CartoDB/fix-parser-underscore-template
Fix parser's use of underscore's template
2016-05-30 16:45:18 +02:00
nobuti
9e8c90b6f9 Bumped version of underscore.
Updated some method calls.
Dist generation files.
2016-05-30 16:06:04 +02:00
nobuti
d3e23dcb5d Fix parser's use of underscore's template method to work with modern use of it. 2016-05-30 14:05:42 +02:00
Raul Ochoa
176886f1ad Use build status from cartodb repo 2016-04-27 15:37:35 +02:00
Raul Ochoa
673cf38121 Add test for non-dot notation filters 2016-04-27 15:23:01 +02:00
Raul Ochoa
860bc0adeb Merge pull request #14 from CartoDB/filter-avoid-dot-notation
Avoid using dot notation to allow complex keys
2016-04-27 15:12:51 +02:00
Raul Ochoa
be56e24d9a Avoid using dot notation to allow complex keys, e.g., mapnik::geometry_type 2016-04-26 18:19:53 +02:00
javi santana
27850ed122 Merge pull request #12 from CartoDB/fixed_strings_with_quote
fixed cartocss with single quoted strings
2016-02-24 12:28:45 +01:00
javi
0d2dddf978 fixed cartocss with single quoted strings 2016-02-24 12:16:29 +01:00
Raul Ochoa
fba91a0633 Merge pull request #11 from CartoDB/missed_small_changes
Missed changes
2015-12-14 17:44:59 +01:00
Young Hahn
1612b5a8b7 Drop xml2js and thus mml2json.
(cherry picked from commit bde0d0e2ab)
2015-11-30 16:14:53 -08:00
Paul Norman
4f13aabb6c Update less link
Update Less page link to match the current layout
(cherry picked from commit 73e6726e089ff47fae5115183aa3776918946ab7)
2015-11-30 13:56:53 -08:00
Raul Ochoa
8050ec843f Release 0.15.1-cdb1 2015-02-20 19:48:15 +01:00
Francisco Dans
885849fe82 Merge pull request #9 from CartoDB/image-filters-fixes
Image filters fixes
2015-01-21 14:41:51 +01:00
Francisco Dans
14c0d3f550 adds color sequence test 2015-01-21 14:38:43 +01:00
Francisco Dans
0f46b57020 returns correct object for color list 2015-01-21 12:54:04 +01:00
Francisco Dans
152954ee70 adds additional filters to ref 2015-01-21 12:53:13 +01:00
Francisco Dans
2631c928b7 adds Makefile to produce uncompressed and minified dists 2015-01-19 14:29:11 +01:00
Francisco Dans
9e3ae6e6fd adds uncompressed version of carto 2015-01-12 18:39:49 +01:00
Francisco Dans
eae8886b95 new dist 2015-01-12 18:35:15 +01:00
Francisco Dans
00c7a631b1 Merge pull request #8 from CartoDB/icon-list
Adding method to extract all "marker-file" properties
2015-01-08 17:35:53 +01:00
Francisco Dans
bd03e0c454 adds basic test for marker-files 2015-01-08 17:30:49 +01:00
Francisco Dans
981e117731 uses proper equality checks 2015-01-08 17:10:43 +01:00
Francisco Dans
d2a557acd9 removes old reference, renames method 2015-01-08 16:40:02 +01:00
Francisco Dans
f91ac22bfc adds method to gather image asset file names 2015-01-08 16:21:24 +01:00
javi
db1cf0a8aa missing torque prop 2014-12-18 11:02:20 +01:00
javi
46b3f4857f version bump 2014-12-18 09:04:03 +01:00
javi
b5b03cc8d7 expose the default reference used 2014-12-18 09:02:13 +01:00
javi
8f86216fe0 updates the reference to work with the new parser
see db3c03bd1a
2014-12-18 09:01:59 +01:00
Raul Ochoa
bbfe3b3084 Merge pull request #6 from CartoDB/browserify_fixes
browserify fixes
2014-12-17 14:47:42 +01:00
javi
56d69ab68a browserify fixes 2014-12-17 14:35:42 +01:00
javi
e491c64ceb improved package.json 2014-12-16 18:39:18 +01:00
Raul Ochoa
26c30d2fb5 Remove doc block for non-existent param 2014-12-09 12:52:42 +01:00
Raul Ochoa
6ae21b3ee0 Merge pull request #4 from CartoDB/remove-run-coverage-on-travis
Remove coverage run from travis configuration as it is failing
2014-12-05 17:36:42 +01:00
Raul Ochoa
12d1b1a4fe Merge pull request #3 from CartoDB/console-output-on-debug-only
Only use console output if debug option is present
2014-12-05 17:36:24 +01:00
Raul Ochoa
8a5f75546f Remove coverage run from travis configuration as it is failing 2014-12-05 13:06:53 +01:00
Raul Ochoa
74d807a3ae Only use console output if debug option is present 2014-12-05 13:01:15 +01:00
Raul Ochoa
bf6537071a Check for style property 2014-12-04 19:09:43 +01:00
Raul Ochoa
1cb891ef92 Return style function from shader 2014-12-04 18:58:37 +01:00
javi
79a770f0af removed unnused files 2014-12-04 18:43:13 +01:00
javi
44610ab1c4 using torque reference 2014-12-04 18:42:30 +01:00
javi
7c35dda115 Merge remote-tracking branch 'origin/browserify' into update_to_master
Conflicts:
	.gitignore
	.travis.yml
	CHANGELOG.md
	Makefile
	README.md
	bin/mml2json.js
	lib/carto/index.js
	lib/carto/parser.js
	lib/carto/tree/call.js
	lib/carto/tree/filterset.js
	lib/carto/tree/reference.js
	lib/carto/tree/ruleset.js
	package.json
	test/errorhandling.test.js
	test/errorhandling/invalid_value.mss
	test/errorhandling/invalid_value.result
	test/errorhandling/issue297.mss
	test/errorhandling/issue297.result
	test/rendering-mss.test.js
	test/rendering/transforms.mss
	test/rendering/transforms.result
	test/specificity/demo.result
	test/specificity/filters_and_ids.result
	test/specificity/issue60.result
	test/version.test.js
2014-12-04 18:35:24 +01:00
javi
146976c8a3 using process.browser instead of type window 2014-12-04 17:29:15 +01:00
javi
ae111041dd making explicit imports 2014-12-04 17:28:54 +01:00
javi
2e00705b64 adds support for browserify
this pr also changes some code to make compatible with the browser. It exposes the global variable "carto"
One of the problems is mapnik reference, the module is not read to work with browserify since it loads modules dynamically. It does not make sense to make it compatible since mapnik-reference is not going to be used so people must to set the reference needed before parse/render:

carto.tree.Reference.setData(reference)
2014-12-04 16:29:20 +01:00
Dane Springmeyer
b19ade3850 use latest mapnik-reference@6.0.2 2014-09-26 13:10:51 -07:00
Dane Springmeyer
fe770630bb Add test for proposed syntax for passing variable to mapnik (just as string) - refs #269 2014-09-25 18:57:48 -07:00
Dane Springmeyer
8df31b4fe6 Merge pull request #373 from mapbox/expressions
Al(most) all symbolizer properties as expressions
2014-09-25 18:56:53 -07:00
Dane Springmeyer
e29b900342 call this branch v0.14.0 2014-09-25 18:17:48 -07:00
Dane Springmeyer
150e7166f6 mapnik ref: default to Mapnik 3.0.0 2014-09-25 18:13:35 -07:00
Dane Springmeyer
1d6e4a6f5d drop node v0.8.x testing on travis 2014-09-25 15:12:54 -07:00
Dane Springmeyer
52343ff833 text-name is an expression so flat string is okay - tweak issue297 to actually be invalid since text-face-name is not currently an expression 2014-09-25 15:06:56 -07:00
Dane Springmeyer
544491b91d quoting of enumerations is okay, so let this be okay to carto tests 2014-09-25 14:58:58 -07:00
Dane Springmeyer
7d3ba895f5 trim rendering results to avoid comparing ending newlines 2014-09-25 14:56:29 -07:00
Dane Springmeyer
d5bf19a64f remove back compat shim now that new mapnik-ref@6.0.0 is tagged 2014-09-25 12:37:52 -07:00
Dane Springmeyer
5890802d6d use new mapnik-reference@6.0.0 2014-09-25 12:33:06 -07:00
Saman Bemel-Benrud
26a918d2e6 Merge pull request #372 from mapbox/fix-formatting
Fix formatting
2014-09-25 12:33:48 -04:00
samanpwbb
1ba5e0035e Re-generate 2014-09-25 12:24:01 -04:00
samanpwbb
1e0657ee1e bump / add newline 2014-09-23 18:58:14 -04:00
samanpwbb
2a5c85a9d5 smaller headings 2014-09-23 18:54:19 -04:00
samanpwbb
cbd4c0250d better rules 2014-09-23 18:52:24 -04:00
samanpwbb
08ca40d3f7 try linebreaks 2014-09-23 18:51:11 -04:00
samanpwbb
9386a56464 whitespace adjustments 2014-09-23 18:47:10 -04:00
samanpwbb
d24998705d adjust docs formatting 2014-09-23 18:45:55 -04:00
Dane Springmeyer
4de08ce68d new mapnik-reference that declares all expressions for mapnik 3.x 2014-09-23 15:31:58 -07:00
samanpwbb
9f65589279 formatting fixes 2014-09-23 18:19:02 -04:00
Saman Bemel-Benrud
6f2ec7b335 Merge pull request #371 from mapbox/no-site
add minimal markdown docs
2014-09-23 18:10:13 -04:00
samanpwbb
89a282be82 add minimal markdown docs 2014-09-23 17:52:36 -04:00
Dane Springmeyer
51baca34ae if test does not error (as expected) then assert false instead of hanging 2014-09-23 14:22:59 -07:00
Young Hahn
481a0fc406 0.13.0 2014-09-04 17:22:19 -04:00
Young Hahn
60e33e609b Merge pull request #366 from mapbox/newref
Use newer mapnik ref
2014-09-04 17:17:10 -04:00
Young Hahn
339d781ca6 Use newer mapnik ref. 2014-09-04 16:34:48 -04:00
Tom MacWright
b7819be42d Merge pull request #365 from mapbox/variable-transforms-test-coverage
Add support for variable transforms - refs #163, and improve test covera...
2014-09-03 15:21:16 -04:00
Tom MacWright
c570c2cd0e Add support for variable transforms - refs #163, and improve test coverage 2014-09-03 15:12:31 -04:00
javi santana
30b4fe1fc6 Merge pull request #2 from CartoDB/update_to_master_fix
env parameter added to several function calls
2014-08-29 10:52:11 +02:00
Jan Marsch
1ddefbe8eb env parameter added to several function calls
filtercondition-value now properly quotes strings
2014-08-25 12:34:29 +02:00
Young Hahn
57ddf46813 0.12.1 2014-08-02 21:47:27 -04:00
Young Hahn
ba720bcb84 Update changelog 2014-08-02 21:42:03 -04:00
Young Hahn
d608fa93b8 0.12.0 2014-08-02 21:41:17 -04:00
Young Hahn
bb81e5c785 Merge pull request #364 from mapbox/no-xml2js
Drop xml2js and thus mml2json
2014-08-02 18:40:27 -07:00
Young Hahn
bde0d0e2ab Drop xml2js and thus mml2json. 2014-08-02 17:40:54 -04:00
Tom MacWright
145f1cc0b1 test color fns 2014-07-03 12:00:40 -04:00
Tom MacWright
2273e0174f Test colors and comments 2014-07-03 11:53:12 -04:00
Tom MacWright
b9a00ed68b Use coveralls and add coverage 2014-07-03 11:44:35 -04:00
javi
a40a87cd39 modified to support rendering order and symbolizer 2014-06-23 18:03:28 +02:00
javi
88bddbabc4 fixed makefile lowercase :( 2014-06-16 18:58:14 +02:00
javi
ef59be8abf fixed filter set (need test) 2014-06-16 18:58:04 +02:00
javi
b83d9c4037 fixed makefile 2014-06-16 18:57:50 +02:00
javi
678157b478 added getShader 2014-06-16 18:57:42 +02:00
javi
1a21c88484 added a test for filter based render 2014-06-16 18:57:24 +02:00
javi
ddb4bd338b moved code where it belongs 2014-06-16 12:54:42 +02:00
javi
6fad5676b4 removed renderer options, just return styles 2014-06-16 12:45:17 +02:00
javi
6dc65cc991 update to mapbox 0.0.11 master 2014-06-11 17:52:06 +02:00
javi
10b81aa2c8 updated doc 2014-06-06 10:04:19 +02:00
javi
2fc7473a8c added rectangle as marker-type 2014-06-06 10:04:11 +02:00
Young Hahn
b675a648c0 0.11.0 2014-05-20 17:15:09 -04:00
Young Hahn
e0fe7bce17 Merge pull request #346 from mapbox/sync
Sync
2014-05-20 17:12:17 -04:00
Young Hahn
eb2623d677 Add tests that run the carto binary. 2014-05-20 17:00:37 -04:00
Young Hahn
302d409fab Update error handling tests. 2014-05-20 16:10:31 -04:00
Young Hahn
c5d8f4510e Switch carto to be synchronous. 2014-05-20 16:01:43 -04:00
Young Hahn
cd18bea7ba 0.10.0 2014-05-15 00:25:17 -04:00
Young Hahn
55b70b86c8 Merge pull request #342 from mapbox/no-max-extent
Nuke forced maximum-extent
2014-05-14 19:52:17 -04:00
Young Hahn
d9f97d3202 Nuke forced maximum-extent property to enable buffered geometries beyond visual extent. 2014-05-08 16:37:12 -04:00
Dane Springmeyer
a52412c41e Merge pull request #340 from scw/patch-1
minor typos.
2014-04-28 18:16:09 -07:00
Shaun Walbridge
be4e687aa3 minor typos. 2014-04-28 21:02:08 -04:00
Dane Springmeyer
4b386326c7 don't output style if opacity and comp-op are defaults - refs #219 and #339 2014-04-28 16:05:59 -07:00
Dane Springmeyer
4f771ed2a5 fix #339 2014-04-28 15:57:51 -07:00
Dane Springmeyer
580e946cc0 add currently failing testcase for #339 2014-04-28 15:56:52 -07:00
Dane Springmeyer
5dc4610785 use latest mapnik-reference 2014-04-25 16:11:28 -07:00
Dane Springmeyer
9d44691111 fix #313 2014-04-17 10:38:32 -07:00
Dane Springmeyer
bb2c045325 add testcase for #315 if I'm understanding right should produce no output 2014-04-10 22:20:33 -04:00
Dane Springmeyer
9f18e9cc2c bump to v0.9.6 in package.json 2014-04-10 22:10:23 -04:00
Dane Springmeyer
c6f787f761 add v0.9.6 to changelog 2014-04-10 22:06:27 -04:00
Dane Springmeyer
cfab4f6369 fix #303 2014-04-10 22:03:09 -04:00
Dane Springmeyer
fb06bb4bb1 fix #319 2014-04-10 21:51:19 -04:00
Dane Springmeyer
87f57bdb38 Merge pull request #319 from mojodna/text-face-name-escaping
[bug] text-face-names containing &s aren't escaped properly
2014-04-10 21:50:25 -04:00
Dane Springmeyer
b9309a7f80 Merge pull request #308 from strk/aposquote
Fix escaping of single quote.
2014-04-10 21:22:50 -04:00
Dane Springmeyer
96886c73c6 use mapnik-reference@5.0.8 2014-04-10 21:07:47 -04:00
Dane Springmeyer
443b81012e upgrade underscore and xml2js 2014-04-10 21:02:00 -04:00
Dane Springmeyer
f691a47306 more readme modernization tweaks 2014-04-07 16:56:25 -04:00
Dane Springmeyer
02a657f373 modernize readme and move cascadenik mentions to https://github.com/mapbox/carto/wiki/Differences-With-Cascadenik 2014-04-07 16:49:27 -04:00
Dane Springmeyer
a5e5c045c9 add support for image-filters-inflate - refs mapnik/mapnik#2165 2014-02-27 10:41:43 -08:00
javi
c288d09dcc dist file 2014-02-25 09:53:20 +01:00
javi
5c8963f02d added source-over to reference 2014-02-25 09:53:15 +01:00
Dane Springmeyer
12a3d6cad2 start using mapnik-reference master 2014-02-19 11:57:58 -08:00
Tom MacWright
bbdb6e5988 Merge pull request #325 from iwillig/iwillig/fix-jsdocs
IW: Fix docs strings
2014-02-07 08:04:55 -05:00
Ivan Willig
f6a76ec666 IW: Fix docs strings 2014-02-07 07:41:03 -05:00
javi
83c478875f backport from master to support unsigned types 2014-01-23 17:46:13 +01:00
javi
449be47e91 dist file 2014-01-21 10:17:27 +01:00
javi
a240695b65 added torque properties to general reference 2014-01-21 10:17:22 +01:00
Dane Springmeyer
84618be5df travis: stop testing on node v0.6.x as per isaacs/npm#4379 2014-01-07 16:04:10 -08:00
samanpwbb
f333705cc2 revert rename 2013-12-09 20:39:54 -05:00
javi
f45aa875b7 fixed keywords 2013-11-26 18:39:07 +01:00
javi
834985ba81 dist files 2013-11-25 16:36:25 +01:00
javi
d1c6c9fb82 fixed rendering with variables 2013-11-25 16:36:14 +01:00
Seth Fitzsimmons
0b584a84e7 Failing test for text-face-names containing &s 2013-11-22 16:36:57 -08:00
javi
fadcb3391d added utility function eval to get the real value for a property 2013-11-18 17:31:18 +01:00
javi
66d7c45a0c fixed string eval 2013-11-18 17:31:00 +01:00
Tom MacWright
65a7d6589e Merge pull request #314 from strk/exponent-in-filter
Support exponential notation in filter values
2013-11-07 07:09:11 -08:00
Sandro Santilli
ce4c61cc7f Support exponential notation in filter values
Closes #311
Includes testcase
2013-11-07 12:48:19 +01:00
javi
677ed7122a dist file 2013-11-05 08:52:14 +01:00
javi
f3fd24583d fixed IE7 and IE8 script loading 2013-11-05 08:52:08 +01:00
Sandro Santilli
60030b1e69 Fix escaping of single quote. Closes #307, updates tests. 2013-10-03 13:40:50 +02:00
javi
d99c5d6c14 dist files 2013-08-28 12:37:51 +02:00
javi
2bfc8a154b uniq frames 2013-08-28 12:37:45 +02:00
javi
5a8461a6c1 fixed renderer 2013-08-28 12:12:06 +02:00
javi
dfacf064e8 added frame offset 2013-08-28 11:52:18 +02:00
javi
6562d77935 fixed js renderer 2013-08-27 17:34:55 +02:00
javi
d9e412696d dist files 2013-08-05 18:16:26 +02:00
javi
08db949fbf fixes mapbox/carto#288 2013-08-05 18:16:10 +02:00
javi
e32aeb9fbb dist files 2013-07-31 16:53:38 +02:00
javi
d7d49214cd support for uris in javascript compiler 2013-07-31 16:53:33 +02:00
javi
ca42300648 dist 2013-07-31 12:43:09 +02:00
javi
393fc249b2 be able to set the the reference 2013-07-31 12:33:39 +02:00
javi
2ae7743c7d created assert stub for the browser 2013-07-31 12:33:19 +02:00
javi
1d752708c4 improved makefile 2013-07-31 12:32:55 +02:00
javi
4f963b8bb3 error management, an exception is thrown when there is a parse error 2013-07-29 13:55:31 +02:00
javi
bc9d67b328 dist file 2013-07-29 12:43:05 +02:00
javi
656b6db9a3 added rendered to transform cartocss to javascript object 2013-07-29 12:42:59 +02:00
javi
a57a162248 fixed build 2013-07-29 12:42:41 +02:00
javi
3d87cd490d fixed global imports 2013-07-11 16:14:32 +02:00
javi
97f03a2eb3 added functions to browser build 2013-06-26 14:50:53 +02:00
javi
99ee75ab8b added tree functions to cart build for browser 2013-06-24 17:15:35 +02:00
javi
264925324f updated reference to 2.1 2013-06-13 15:42:47 +02:00
javi
d81b4b9d18 browser version working 2013-06-10 13:26:19 +02:00
javi
6fb339e21c test files 2012-07-25 17:42:49 +02:00
javi
4bb0fc0ebe changed some stuff to get cart working on the browser 2012-07-25 17:42:41 +02:00
Tom MacWright
bfc92c26bc Stubbing out assert, trying to get higher-level stylesheets 2012-07-18 17:27:19 +02:00
Tom MacWright
f11259d734 Carto, with a few small tweaks, starting to operate in-browser 2012-07-18 17:27:19 +02:00
cloudhead
2481c21365 deleted compiler.jar 2012-07-18 17:23:52 +02:00
cloudhead
e7a3d65cad Added make min for minification though google's js closure compiler. 2012-07-18 17:23:51 +02:00
186 changed files with 24637 additions and 767 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
test/rendering/layers/
test/rendering/cache/
test/rendering-mss/npm-debug.log
.idea/

View File

@ -1,7 +1,9 @@
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
- "0.6"
- '6'
- '8'
- '10'
script:
- npm test

9
CHANGELOG.carto.md Normal file
View File

@ -0,0 +1,9 @@
## CARTO's Changelog
## 0.15.1-cdb5
2018-11-20
* Support Node.js 6, 8 and, 10
* Drop support for Node.js 0.10 and 0.11
* Add package-lock.json
* Add CHANGELOG.carto.md

View File

@ -1,5 +1,36 @@
## Changelog
## 0.14.0
* Support for Mapnik 3.x
* Bump `mapnik-reference` dependency to ~6.0.1.
## 0.13.0
* Allows optional args in transforms.
* Bump `mapnik-reference` dependency to 5.1.x.
## 0.12.0
* Drop mml2json and xml2js dependency.
## 0.11.0
* Switch API to be synchronous. All errors should be caught using try/catch now.
## 0.10.0
* Remove automatic inclusion of `maximum-extent` on Map element to allow geometries that are buffered past extent bounds (e.g. dateline).
* Bump `mapnik-reference` dependency to ~5.0.9 (with `shield-halo-rasterizer`)
## 0.9.6
* Fixed support for `text-face-name` values with `&` like `El&Font Bubble`
* Fixed support for filtering on fields containing single quotes. Now `#layer[name="it's"] { ... }` is possible.
* Fixed support for filtering on fields containing `&`. Now `#layer["Hello&Goodbye"="yes"] { ... }` is possible.
* Added support for exponential notation in filters. Now `#layer[value = 1.2e3] { ... }` is possible.
* Bump `mapnik-reference` dependency to ~5.0.8 (with support for Mapnik v2.3.0 and 3.x)
## 0.9.5
* Various speed optimizations to help address #20 (#231)

View File

@ -16,7 +16,7 @@ Running the head binary:
## Documentation
This repository contains auto-generated documentation of the content of Carto
that's published on MapBox.com.
that's published on Mapbox.com.
git fetch origin gh-pages:gh-pages

View File

@ -3,6 +3,15 @@
#
expresso = ./node_modules/.bin/mocha
UGLIFYJS=./node_modules/.bin/uglifyjs
BROWSERIFY = ./node_modules/.bin/browserify
dist/carto.js: dist/carto.uncompressed.js $(shell $(BROWSERIFY) --list lib/carto/index.js)
$(UGLIFYJS) dist/carto.uncompressed.js > $@
dist/carto.uncompressed.js: dist $(shell $(BROWSERIFY) --list lib/carto/index.js)
$(BROWSERIFY) lib/carto/index.js --exclude node_modules/underscore/underscore.js --standalone carto > $@
lint:
./node_modules/.bin/jshint lib/carto/*.js lib/carto/tree/*.js
@ -17,4 +26,10 @@ endif
check: test
dist:
mkdir -p dist
.PHONY: test

372
README.md
View File

@ -1,307 +1,83 @@
# CartoCSS
[![Build Status](https://secure.travis-ci.org/mapbox/carto.png)](http://travis-ci.org/mapbox/carto)
[![Build Status](https://travis-ci.org/CartoDB/carto.png?branch=master)](https://travis-ci.org/CartoDB/carto)
Is as stylesheet renderer for javascript, It's an evolution of the Mapnik renderer from Mapbox.
Please, see original [Mapbox repo](http://github.com/mapbox/carto) for more information and credits
## Quick Start
```javascript
// shader is a CartoCSS object
var cartocss = [
'#layer {',
' marker-width: [property]',
' marker-fill: red',
'}'
].join('')
var shader = new carto.RendererJS().render(cartocss);
var layers = shader.getLayers()
for (var i = 0; i < layers.length; ++i) {
var layer = layers[i];
console.log("layer name: ", layer.fullName())
console.log("- frames: ", layer.frames())
console.log("- attachment: ", layer.attachment())
var layerShader = layer.getStyle({ property: 1 }, { zoom: 10 })
console.log(layerShader['marker-width']) // 1
console.log(layerShader['marker-fill']) // #FF0000
}
```
# API
## RendererJS
### render(cartocss)
## CartoCSS
compiled cartocss object
### getLayers
return the layers, an array of ``CartoCSS.Layer`` object
### getDefault
return the default layer (``CartoCSS.Layer``), usually the Map layer
### findLayer(where)
find a layer using where object.
```
shader.findLayer({ name: 'test' })
```
## CartoCSS.Layer
### getStyle(props, context)
return the evaluated style:
- props: object containing properties needed to render the style. If the cartocss style uses
some variables they should be passed in this object
- context: rendering context variables like ``zoom`` or animation ``frame``
Is a stylesheet renderer for Mapnik. It's an evolution of the
[Cascadenik](https://github.com/mapnik/Cascadenik) idea and language,
with an emphasis on speed and flexibility.
## Reference Documentation
* [mapbox.com/carto](http://mapbox.com/carto/)
## MML
_incompatibility_
* MML files are assumed to be JSON, not XML. The files are near-identical
to the XML files accepted by Cascadenik, just translated into JSON.
* CartoCSS will not embed files or download URLs for you. Stylesheets should
be embedded directly into your MML JSON and any datasources should be
paths (relative or absolute) that would be acceptable in Mapnik XML.
The [millstone project](https://github.com/mapbox/millstone) aims to fill this need.
CartoCSS MML:
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [{"id":"style.mss","data":"Map {\n background-color: #fff;\n}\n\n#world {\n line-color: #ccc;\n line-width: 0.5;\n polygon-fill: #eee;\n}"}],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=latlong +ellps=WGS84 +datum=WGS84 +no_defs",
"Datasource": {
"file": "world_borders",
"type": "shape"
}
}]
}
Cascadenik MML
<pre>&lt;Stylesheet&gt;&lt;![CDATA[
Map {
map-bgcolor: #69f;
}
Layer {
line-width: 1;
line-color: #696;
polygon-fill: #6f9;
}
]]&gt;&lt;/Stylesheet&gt;
&lt;Layer srs=&quot;+proj=latlong +ellps=WGS84 +datum=WGS84 +no_defs&quot;&gt;
&lt;Datasource&gt;
&lt;Parameter name=&quot;type&quot;&gt;shape&lt;/Parameter&gt;
&lt;Parameter name=&quot;file&quot;&gt;world_borders&lt;/Parameter&gt;
&lt;/Datasource&gt;
&lt;/Layer&gt;
&lt;/Map&gt;</pre>
## Attachments and Instances
_new_
In CSS, a certain object can only have one instance of a property. A `<div>` has a specific border width and color, rules that match better than others (#id instead of .class) override previous definitions. `CartoCSS` acts the same way normally for the sake of familiarity and organization, but Mapnik itself is more powerful.
Layers in Mapnik can have multiple [borders](http://trac.mapnik.org/wiki/LineSymbolizer) and multiple copies of other attributes. This ability is useful in drawing line outlines, like in the case of road borders or 'glow' effects around coasts. `CartoCSS` makes this accessible by allowing attachments to styles:
#world {
line-color: #fff;
line-width: 3;
}
#world::outline {
line-color: #000;
line-width: 6;
}
Attachments are optional: if you don't define them, CartoCSS does overriding of styles just like Cascadenik.
This brings us to another _incompatibility_: `line-inline` and `line-outline` have been removed from the language, because attachments are capable of the same trick.
While attachments allow creating implicit "layers" with the same data, using **instances** allows you to create multiple symbolizers in the same style/layer:
#roads {
casing/line-width: 6;
casing/line-color: #333;
line-width: 4;
line-color: #666;
}
This makes Mapnik first draw the line of color #333 with a width of 6, and then immediately afterwards, it draws the same line again with width 4 and color #666. Contrast that to attachments: Mapnik would first draw all casings before proceeding to the actual lines.
## text-name
_incompatibility_
Instead of the name attribute of the [TextSymbolizer](http://trac.mapnik.org/wiki/TextSymbolizer) and [ShieldSymbolizer](http://trac.mapnik.org/wiki/ShieldSymbolizer) being a part of the selector, it is a property of a rule. Thus the evaluation is less complex and one can use expressions in names.
<table>
<tr>
<th>cascadenik</th>
<th>CartoCSS</th>
</tr>
<tr>
<td valign='top'>
<pre>
#world NAME {
text-face-name: "Arial";
}</pre>
</td>
<td valign='top'>
<pre>
#world {
text-name: "NAME";
text-face-name: "Arial";
}</pre>
</td>
</tr>
</table>
## Mapnik
_new_
CartoCSS is only compatible with Mapnik >= 2.x.x.
## Rasters and Buildings
_new_
Rasters are supported in CartoCSS - it knows how to download `.vrt`, `.tiff`, and soon other raster formats, and the properties of the [RasterSymbolizer](http://trac.mapnik.org/wiki/RasterSymbolizer) are exposed in the language.
The [BuildingSymbolizer](http://trac.mapnik.org/wiki/BuildingSymbolizer) is also supported in `CartoCSS`. The code stores symbolizer types and properties in a JSON file (in `tree/reference.json`), so new Mapnik features can be quickly implemented here.
## Variables & Expressions
_new_
CartoCSS inherits from its basis in [less.js](http://lesscss.org/) some new features in CSS. One can define variables in stylesheets, and use expressions to modify them.
@mybackground: #2B4D2D;
Map {
background-color: @mybackground
}
#world {
polygon-fill: @mybackground + #222;
line-color: darken(@mybackground, 10%);
}
## Nested Styles
_new_
CartoCSS also inherits nesting of rules from less.js.
/* Applies to all layers with .land class */
.land {
line-color: #ccc;
line-width: 0.5;
polygon-fill: #eee;
/* Applies to #lakes.land */
#lakes {
polygon-fill: #000;
}
}
This can be a convenient way to group style changes by zoom level:
[zoom > 1] {
/* Applies to all layers at zoom > 1 */
polygon-gamma: 0.3;
#world {
polygon-fill: #323;
}
#lakes {
polygon-fill: #144;
}
}
## FontSets
_new_
By defining multiple fonts in a `text-face-name` definition, you create [FontSets](http://trac.mapnik.org/wiki/FontSet) in `CartoCSS`. These are useful for supporting multiple character sets and fallback fonts for distributed styles.
<table>
<tr>
<th>carto</th><th>XML</th>
</tr>
<tr>
<td valign='top'>
<pre>#world {
text-name: "[NAME]";
text-size: 11;
text-face-name: "Georgia Regular", "Arial Italic";
}</pre>
</td>
<td valign='top'>
<pre>&lt;FontSet name=&quot;fontset-0&quot;&gt;
&lt;Font face-name=&quot;Georgia Regular&quot;/&gt;
&lt;Font face-name=&quot;Arial Italic&quot;/&gt;
&lt;/FontSet&gt;
&lt;Style name=&quot;world-text&quot;&gt;
&lt;Rule&gt;
&lt;TextSymbolizer fontset-name=&quot;fontset-0&quot;
size=&quot;11&quot;
name=&quot;[NAME]&quot;/&gt;
&lt;/Rule&gt;
&lt;/Style&gt;</pre>
</td>
<tr>
</table>
## Filters
CartoCSS supports a variety of filter styles:
Numeric comparisons:
```
#world[population > 100]
#world[population < 100]
#world[population >= 100]
#world[population <= 100]
```
General comparisons:
```
#world[population = 100]
#world[population != 100]
```
String comparisons:
```
/* a regular expression over name */
#world[name =~ "A.*"]
```
## Developers
#### Installation
If you're using [TileMill](http://mapbox.com/tilemill/), you're already
using CartoCSS and don't need to do a thing.
If you're a developer-type and want to use the `carto` binary with
`node.js` (and you have [npm](http://npmjs.org/) installed),
npm install -g carto
#### From the binary
Install `millstone` to enable support for localizing external resources (URLs and local files) referenced in your mml file.
npm install millstone
carto map_file.json
#### From code
Currently CartoCSS is designed to be invoked from [node.js](http://nodejs.org/).
The `Renderer` interface is the main API for developers, and it takes an MML file as a string as input.
// defined variables:
// - input (the name or identifier of the file being parsed)
// - data (a string containing the MML or an object of MML)
var carto = require('carto');
new carto.Renderer({
filename: input,
local_data_dir: path.dirname(input),
}).render(data, function(err, output) {
if (err) {
if (Array.isArray(err)) {
err.forEach(function(e) {
carto.writeError(e, options);
});
} else { throw err; }
} else {
sys.puts(output);
}
});
### Vim
To install, download or clone this repository, then add the `vim-carto`
directory located at `build/vim-carto` to your `~/.vim` file.
## Credits
CartoCSS is based on [less.js](https://github.com/cloudhead/less.js), a CSS compiler written by Alexis Sellier.
It depends on:
* [underscore.js](https://github.com/documentcloud/underscore/)
Only for running tests:
* [mocha](https://github.com/visionmedia/mocha)
* [sax-js](https://github.com/isaacs/sax-js/)
## Authors
* Tom MacWright (tmcw)
* Konstantin Käfer (kkaefer)
* AJ Ashton (ajashton)
* Dane Springmeyer (springmeyer)

View File

@ -65,26 +65,13 @@ function compileMML(err, data) {
console.error(err);
process.exit(1);
}
var renderer = new carto.Renderer({
filename: input,
benchmark: options.benchmark,
ppi: options.ppi
});
try {
var renderer = new carto.Renderer({
filename: input,
benchmark: options.benchmark,
ppi: options.ppi
});
renderer.render(data, function(err, output) {
if (err) {
console.error(err);
throw err;
process.exit(1);
} else {
if (!options.benchmark) {
console.log(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
}
});
var output = renderer.render(data);
} catch (e) {
if (e.stack) {
console.error(e.stack);
@ -93,6 +80,12 @@ function compileMML(err, data) {
}
process.exit(1);
}
if (!options.benchmark) {
console.log(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
};
function compileMSS(err, data) {
@ -100,26 +93,13 @@ function compileMSS(err, data) {
console.error(err);
process.exit(1);
}
var renderer = new carto.Renderer({
filename: path.basename(input),
benchmark: options.benchmark,
ppi: options.ppi
});
try {
var renderer = new carto.Renderer({
filename: path.basename(input),
benchmark: options.benchmark,
ppi: options.ppi
});
renderer.renderMSS(data, function(err, output) {
if (err) {
console.error(err);
throw err;
process.exit(1);
} else {
if (!options.benchmark) {
console.log(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
}
});
var output = renderer.renderMSS(data);
} catch (e) {
if (e.stack) {
console.error(e.stack);
@ -128,6 +108,12 @@ function compileMSS(err, data) {
}
process.exit(1);
}
if (!options.benchmark) {
console.log(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
};
try {

View File

@ -1,69 +0,0 @@
#!/usr/bin/env node
var xml2js = require('xml2js'),
fs = require('fs');
if (!process.argv[2]) {
console.log('Please specify a XML file.');
process.exit(1);
}
fs.readFile(process.argv[2], 'utf-8', function(err, data) {
if (err) throw err;
// Replace entities.
var entities = {};
var match = data.match(/<!ENTITY([^>]|"([^"]|\\")*")+>/g)
if (match != null) {
match.forEach(function(entity) {
var parts = entity.match(/^<!ENTITY\s+(\w+)\s+"(.+)">$/);
entities['&' + parts[1] + ';'] = parts[2];
});
}
data = data.replace(/&\w+;/g, function(entity) {
return entities[entity];
});
function addAttributes(obj) {
if (obj['$']) for (var key in obj['$']) obj[key] = obj['$'][key];
delete obj['$'];
return obj;
}
function simplifyExternal(obj) {
if (obj.src) return obj.src;
else return obj;
}
var parser = new xml2js.Parser({
explicitRoot: false,
explicitArray: false
});
parser.addListener('end', function(json) {
console.log(JSON.stringify(json, function(key, value) {
if (!key) {
return addAttributes(value);
}
else if (key === 'Stylesheet') {
if (Array.isArray(value)) return value.map(addAttributes).map(simplifyExternal);
else return [ simplifyExternal(addAttributes(value)) ];
}
else if (key === 'Layer' || key === 'Stylesheet') {
if (Array.isArray(value)) return value.map(addAttributes);
else return [ addAttributes(value) ];
}
else if (key === 'Datasource') {
value = addAttributes(value);
value.Parameter.forEach(function(parameter) {
value[parameter['$'].name] = parameter['_'];
});
delete value.Parameter;
return value;
}
else {
return value;
}
}, 4));
});
parser.parseString(data);
});

7
build/header.js Normal file
View File

@ -0,0 +1,7 @@
//
// LESS - Leaner CSS v@VERSION
// http://lesscss.org
//
// Copyright (c) 2010, Alexis Sellier
// Licensed under the MIT license.
//

4
dist/carto.js vendored Normal file

File diff suppressed because one or more lines are too long

7375
dist/carto.uncompressed.js vendored Normal file

File diff suppressed because it is too large Load Diff

15
docs-generator/README.md Normal file
View File

@ -0,0 +1,15 @@
# Generating CartoCSS docs
From the `docs-generator/` directory:
```
$ npm install
```
Then:
```
$ node generate.js
```
Will save docs to `docs/`.

View File

@ -0,0 +1,23 @@
var fs = require('fs'),
path = require('path'),
refs = require('mapnik-reference'),
_ = require('underscore');
function tmpl(x) {
return _.template(fs.readFileSync(path.join(__dirname, x), 'utf-8'));
}
var index = tmpl('index._');
var table = tmpl('symbolizers._');
var versions = Object.keys(refs.version);
for (var v in refs.version) {
var ref = refs.version[v];
fs.writeFileSync(path.join(__dirname, '../docs/' + v + '.md'), index({
symbolizers: ref.symbolizers,
table: table,
version: v,
versions: versions,
_: _
}));
}

144
docs-generator/index._ Normal file
View File

@ -0,0 +1,144 @@
# Carto documentation
The following is a list of properties provided in CartoCSS that you can apply to map elements.
<%= table({symbolizers:symbolizers}) %>
### Values
Below is a list of values and an explanation of any expression that can be applied to properties in CartCSS.
### Color
CartoCSS accepts a variety of syntaxes for colors - HTML-style hex values, rgb, rgba, hsl, and hsla. It also supports the predefined HTML colors names, like `yellow` and `blue`.
``` css
#line {
line-color: #ff0;
line-color: #ffff00;
line-color: rgb(255, 255, 0);
line-color: rgba(255, 255, 0, 1);
line-color: hsl(100, 50%, 50%);
line-color: hsla(100, 50%, 50%, 1);
line-color: yellow;
}
```
Especially of note is the support for hsl, which can be [easier to reason about than rgb()](http://mothereffinghsl.com/). Carto also includes several color operation functions [borrowed from less](http://lesscss.org/functions/#color-operations):
``` css
// lighten and darken colors
lighten(#ace, 10%);
darken(#ace, 10%);
// saturate and desaturate
saturate(#550000, 10%);
desaturate(#00ff00, 10%);
// increase or decrease the opacity of a color
fadein(#fafafa, 10%);
fadeout(#fefefe, 14%);
// spin rotates a color around the color wheel by degrees
spin(#ff00ff, 10);
// mix generates a color in between two other colors.
mix(#fff, #000, 50%);
```
These functions all take arguments which can be color variables, literal colors, or the results of other functions operating on colors.
### Float
Float is a fancy way of saying 'number'. In CartoCSS, you specify _just a number_ - unlike CSS, there are no units, but everything is specified in pixels.
``` css
#line {
line-width: 2;
}
```
It's also possible to do simple math with number values:
``` css
#line {
line-width: 4 / 2; // division
line-width: 4 + 2; // addition
line-width: 4 - 2; // subtraction
line-width: 4 * 2; // multiplication
line-width: 4 % 2; // modulus
}
```
### URI
URI is a fancy way of saying URL. When an argument is a URI, you use the same kind of `url('place.png')` notation that you would with HTML. Quotes around the URL aren't required, but are highly recommended. URIs can be paths to places on your computer, or on the internet.
```css
#markers {
marker-file: url('marker.png');
}
```
### String
A string is basically just text. In the case of CartoCSS, you're going to put it in quotes. Strings can be anything, though pay attention to the cases of `text-name` and `shield-name` - they actually will refer to features, which you refer to by putting them in brackets, as seen in the example below.
```css
#labels {
text-name: "[MY_FIELD]";
}
```
### Boolean
Boolean means yes or no, so it accepts the values `true` or `false`.
```css
#markers {
marker-allow-overlap:true;
}
```
### Expressions
Expressions are statements that can include fields, numbers, and other types in a really flexible way. You have run into expressions before, in the realm of 'fields', where you'd specify `"[FIELD]"`, but expressions allow you to drop the quotes and also do quick addition, division, multiplication, and concatenation from within Carto syntax.
```css
#buildings {
building-height: [HEIGHT_FIELD] * 10;
}
```
### Numbers
Numbers are comma-separated lists of one or more number in a specific order. They're used in line dash arrays, in which the numbers specify intervals of line, break, and line again.
```css
#disputedboundary {
line-dasharray: 1, 4, 2;
}
```
### Percentages
In Carto, the percentage symbol, `%` universally means `value/100`. It's meant to be used with ratio-related properties, like opacity rules.
_You should not use percentages as widths, heights, or other properties - unlike CSS, percentages are not relative to cascaded classes or page size, they're, as stated, simply the value divided by one hundred._
```css
#world {
// this syntax
polygon-opacity: 50%;
// is equivalent to
polygon-opacity: 0.5;
}
```
### Functions
Functions are comma-separated lists of one or more functions. For instance, transforms use the `functions` type to allow for transforms within Carto, which are optionally chainable.
```css
#point {
point-transform: scale(2, 2);
}
```

View File

@ -0,0 +1,20 @@
{
"name": "carto-site",
"private": true,
"version": "0.0.0",
"description": "Mapnik Stylesheet Compiler",
"url": "https://github.com/mapbox/carto",
"repositories": [{
"type": "git",
"url": "http://github.com/mapbox/carto.git"
}],
"author": {
"name": "MapBox",
"url": "http://mapbox.com/",
"email": "info@mapbox.com"
},
"dependencies": {
"mapnik-reference": "5.0.x",
"underscore": "~1.3.3"
}
}

View File

@ -0,0 +1,13 @@
<% _(symbolizers).each(function(symbolizer, name) { %>
<% if (name == '*') { %>## All elements<% } else { %>## <%= name %><% } %>
<% _(symbolizer).chain().filter(function(p) { return p.css; }).each(function(p) { %>
##### <%= p.css.replace(/\s/g, '') %> <% if (_.isArray(p.type)) { %>`keyword`<% } else { %>`<%= p.type %>`<% } %>
<% if (_.isArray(p.type)) { %><% _(p.type).each(function(type) { %>`<%= type %>`<% }); %><% } %>
<% if (typeof p['default-value'] !== '') { %>Default Value: <%= p['default-value'] %><% } %>
<% if (p['default-meaning']) { %>_(<%- p['default-meaning'] %>)_<% } %>
<% if (typeof p['range'] !== 'undefined') { %>Range: <%= '' + p['range'] %><% } %>
<% if (p.doc) { %><%- p.doc%><% } %>
* * *
<% }); %>
<% }); %>

1159
docs/2.0.0.md Normal file

File diff suppressed because it is too large Load Diff

1159
docs/2.0.1.md Normal file

File diff suppressed because it is too large Load Diff

1159
docs/2.0.2.md Normal file

File diff suppressed because it is too large Load Diff

1486
docs/2.1.0.md Normal file

File diff suppressed because it is too large Load Diff

1495
docs/2.1.1.md Normal file

File diff suppressed because it is too large Load Diff

1687
docs/latest.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,9 @@ var util = require('util'),
function getVersion() {
if (parseInt(process.version.split('.')[1], 10) > 4) {
if (process.browser) {
return require('../../package.json').version.split('.');
} else if (parseInt(process.version.split('.')[1], 10) > 4) {
return require('../../package.json').version.split('.');
} else {
// older node
@ -18,6 +20,8 @@ var carto = {
Parser: require('./parser').Parser,
Renderer: require('./renderer').Renderer,
tree: require('./tree'),
RendererJS: require('./renderer_js'),
default_reference: require('./torque-reference'),
// @TODO
writeError: function(ctx, options) {
@ -49,7 +53,7 @@ var carto = {
if (typeof(extract[2]) === 'string') {
error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
}
error = options.indent + error.join('\n' + options.indent) + '\033[0m\n';
error = options.indent + error.join('\n' + options.indent) + '\x1B[0m\n';
message = options.indent + message + stylize(ctx.message, 'red');
if (ctx.filename) (message += stylize(' in ', 'red') + ctx.filename);
@ -64,15 +68,34 @@ var carto = {
}
};
[ 'call', 'color', 'comment', 'definition', 'dimension',
'element', 'expression', 'filterset', 'filter', 'field',
'keyword', 'layer', 'literal', 'operation', 'quoted', 'imagefilter',
'reference', 'rule', 'ruleset', 'selector', 'style', 'url', 'value',
'variable', 'zoom', 'invalid', 'fontset'
].forEach(function(n) {
require('./tree/' + n);
});
require('./tree/call');
require('./tree/color');
require('./tree/comment');
require('./tree/definition');
require('./tree/dimension');
require('./tree/element');
require('./tree/expression');
require('./tree/filterset');
require('./tree/filter');
require('./tree/field');
require('./tree/keyword');
require('./tree/layer');
require('./tree/literal');
require('./tree/operation');
require('./tree/quoted');
require('./tree/imagefilter');
require('./tree/reference');
require('./tree/rule');
require('./tree/ruleset');
require('./tree/selector');
require('./tree/style');
require('./tree/url');
require('./tree/value');
require('./tree/variable');
require('./tree/zoom');
require('./tree/invalid');
require('./tree/fontset');
require('./tree/frame_offset');
require('./functions');
for (var k in carto) { exports[k] = carto[k]; }
@ -88,6 +111,6 @@ function stylize(str, style) {
'red' : [31, 39],
'grey' : [90, 39]
};
return '\033[' + styles[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm';
return '\x1B[' + styles[style][0] + 'm' + str +
'\x1B[' + styles[style][1] + 'm';
}

View File

@ -1,6 +1,6 @@
var carto = exports,
tree = require('./tree'),
_ = require('underscore');
_ = global._ || require('underscore');
// Token matching is done with the `$` function, which either takes
// a terminal string or regexp, or a non-terminal function to call.
@ -102,6 +102,11 @@ carto.Parser = function Parser(env) {
}
}
function extractErrorLine(style, errorIndex) {
return (style.slice(0, errorIndex).match(/\n/g) || '').length + 1;
}
// Make an error object from a passed set of properties.
// Accepted properties:
// - `message`: Text of the error message.
@ -109,8 +114,9 @@ carto.Parser = function Parser(env) {
// - `index`: Char. index where the error occurred.
function makeError(err) {
var einput;
var errorTemplate;
_(err).defaults({
_.defaults(err, {
index: furthest,
filename: env.filename,
message: 'Parse error.',
@ -124,11 +130,12 @@ carto.Parser = function Parser(env) {
einput = input;
}
err.line = (einput.slice(0, err.index).match(/\n/g) || '').length + 1;
err.line = extractErrorLine(einput, err.index);
for (var n = err.index; n >= 0 && einput.charAt(n) !== '\n'; n--) {
err.column++;
}
return new Error(_('<%=filename%>:<%=line%>:<%=column%> <%=message%>').template(err));
errorTemplate = _.template('<%=filename%>:<%=line%>:<%=column%> <%=message%>');
return new Error(errorTemplate(err));
}
this.env = env = env || {};
@ -138,6 +145,8 @@ carto.Parser = function Parser(env) {
// The Parser
parser = {
extractErrorLine: extractErrorLine,
//
// Parse an input string into an abstract syntax tree.
// Throws an error on parse errors.
parse: function(str) {
@ -223,13 +232,14 @@ carto.Parser = function Parser(env) {
// Start with the primary rule.
// The whole syntax tree is held under a Ruleset node,
// with the `root` property set to true, so no `{}` are
// output. The callback is called when the input is parsed.
// output.
root = new tree.Ruleset([], $(this.parsers.primary));
root.root = true;
// Get an array of Ruleset objects, flattened
// and sorted according to specificitySort
root.toList = (function() {
var line, lines, column;
return function(env) {
env.error = function(e) {
if (!env.errors) env.errors = new Error('');
@ -462,11 +472,12 @@ carto.Parser = function Parser(env) {
dimension: function() {
var c = input.charCodeAt(i);
if ((c > 57 || c < 45) || c === 47) return;
var value = $(/^(-?\d*\.?\d+)(\%|\w+)?/);
var value = $(/^(-?\d*\.?\d+(?:[eE][-+]?\d+)?)(\%|\w+)?/);
if (value) {
return new tree.Dimension(value[1], value[2], memo);
}
}
},
// The variable part of a variable definition.
@ -517,11 +528,13 @@ carto.Parser = function Parser(env) {
e, elements = [],
f, filters = new tree.Filterset(),
z, zooms = [],
frame_offset = tree.FrameOffset.none;
segments = 0, conditions = 0;
while (
(e = $(this.element)) ||
(z = $(this.zoom)) ||
(fo = $(this.frame_offset)) ||
(f = $(this.filter)) ||
(a = $(this.attachment))
) {
@ -531,6 +544,9 @@ carto.Parser = function Parser(env) {
} else if (z) {
zooms.push(z);
conditions++;
} else if (fo) {
frame_offset = fo;
conditions++;
} else if (f) {
var err = filters.add(f);
if (err) {
@ -554,7 +570,7 @@ carto.Parser = function Parser(env) {
}
if (segments) {
return new tree.Selector(filters, zooms, elements, attachment, conditions, memo);
return new tree.Selector(filters, zooms, frame_offset, elements, attachment, conditions, memo);
}
},
@ -589,6 +605,17 @@ carto.Parser = function Parser(env) {
}
},
frame_offset: function() {
save();
var op, val;
if ($(/^\[\s*frame-offset/g) &&
(op = $(this.entities.comparison)) &&
(val = $(/^\d+/)) &&
$(']')) {
return tree.FrameOffset(op, val, memo);
}
},
zoom: function() {
save();
var op, val;
@ -699,10 +726,22 @@ carto.Parser = function Parser(env) {
},
// A sub-expression, contained by parenthensis
sub: function() {
var e;
var e, expressions = [];
if ($('(') && (e = $(this.expression)) && $(')')) {
return e;
if ($('(')) {
while (e = $(this.expression)) {
expressions.push(e);
if (! $(',')) { break; }
}
$(')');
}
if (expressions.length > 1) {
return new tree.Value(expressions.map(function(e) {
return e.value[0];
}));
} else if (expressions.length === 1) {
return new tree.Value(expressions);
}
},
// This is a misnomer because it actually handles multiplication

View File

@ -1,28 +1,29 @@
var _ = require('underscore');
var _ = global._ || require('underscore');
var carto = require('./index');
carto.Renderer = function Renderer(env, options) {
this.env = env || {};
this.options = options || {};
this.options.mapnik_version = this.options.mapnik_version || 'latest';
this.options.mapnik_version = this.options.mapnik_version || '3.0.0';
};
// Prepare a MSS document (given as an string) into a
// XML Style fragment (mostly useful for debugging)
//
// - @param {String} str the mss contents as a string.
// - @param {Object} env renderer environment options.
carto.Renderer.prototype.renderMSS = function render(data, callback) {
/**
* Prepare a MSS document (given as an string) into a
* XML Style fragment (mostly useful for debugging)
*
* @param {String} data the mss contents as a string.
*/
carto.Renderer.prototype.renderMSS = function render(data) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: []
});
if (!carto.tree.Reference.setVersion(this.options.mapnik_version)) {
return callback(new Error("Could not set mapnik version to " + this.options.mapnik_version), null);
throw new Error("Could not set mapnik version to " + this.options.mapnik_version);
}
var output = [];
@ -56,19 +57,20 @@ carto.Renderer.prototype.renderMSS = function render(data, callback) {
if (env.benchmark) console.timeEnd(bench_name);
}
if (env.benchmark) console.timeEnd('Total Style generation');
if (env.errors) return callback(env.errors);
return callback(null, output.join('\n'));
if (env.errors) throw env.errors;
return output.join('\n');
};
// Prepare a MML document (given as an object) into a
// fully-localized XML file ready for Mapnik2 consumption
//
// - @param {String} str the JSON file as a string.
// - @param {Object} env renderer environment options.
carto.Renderer.prototype.render = function render(m, callback) {
/**
* Prepare a MML document (given as an object) into a
* fully-localized XML file ready for Mapnik2 consumption
*
* @param {String} m - the JSON file as a string.
*/
carto.Renderer.prototype.render = function render(m) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: [],
@ -76,22 +78,21 @@ carto.Renderer.prototype.render = function render(m, callback) {
});
if (!carto.tree.Reference.setVersion(this.options.mapnik_version)) {
return callback(new Error("Could not set mapnik version to " + this.options.mapnik_version), null);
throw new Error("Could not set mapnik version to " + this.options.mapnik_version);
}
var output = [];
// Transform stylesheets into definitions.
var definitions = _(m.Stylesheet).chain()
var definitions = _.chain(m.Stylesheet)
.map(function(s) {
if (typeof s == 'string') {
callback(new Error("Stylesheet object is expected not a string: '" + s + "'"));
throw new Error("Stylesheet object is expected not a string: '" + s + "'");
}
// Passing the environment from stylesheet to stylesheet,
// allows frames and effects to be maintained.
env = _(env).extend({filename:s.id});
env = _.extend(env, {filename:s.id});
// @TODO try/catch?
var time = +new Date(),
root = (carto.Parser(env)).parse(s.data);
if (env.benchmark)
@ -145,19 +146,13 @@ carto.Renderer.prototype.render = function render(m, callback) {
return e.toXML(env);
}).join('\n'));
var map_properties;
try {
map_properties = getMapProperties(m, definitions, env);
} catch (err) {
env.error(err);
return callback(err);
}
var map_properties = getMapProperties(m, definitions, env);
// Exit on errors.
if (env.errors) return callback(env.errors);
if (env.errors) throw env.errors;
// Pass TileJSON and other custom parameters through to Mapnik XML.
var parameters = _(m).reduce(function(memo, v, k) {
var parameters = _.reduce(m, function(memo, v, k) {
if (!v && v !== 0) return memo;
switch (k) {
@ -210,10 +205,7 @@ carto.Renderer.prototype.render = function render(m, callback) {
'\n</Parameters>\n'
);
var properties = _(map_properties).map(function(v) { return ' ' + v; }).join('');
if(!map_properties['maximum-extent']) {
properties += ' maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34"';
}
var properties = _.map(map_properties, function(v) { return ' ' + v; }).join('');
output.unshift(
'<?xml version="1.0" ' +
@ -221,16 +213,17 @@ carto.Renderer.prototype.render = function render(m, callback) {
'<!DOCTYPE Map[]>\n' +
'<Map' + properties +'>\n');
output.push('</Map>');
return callback(null, output.join('\n'));
return output.join('\n');
};
// This function currently modifies 'current'
//
// @param {Array} the current list of rules
// @param {Object} definition a Definition object to add to the rules
// @param {Object} byFilter an object/dictionary of existing filters. This is
// actually keyed `attachment->filter`
// @param {Object} env the current environment
/**
* This function currently modifies 'current'
* @param {Array} current current list of rules
* @param {Object} definition a Definition object to add to the rules
* @param {Object} byFilter an object/dictionary of existing filters. This is
* actually keyed `attachment->filter`
* @param {Object} env the current environment
*/
function addRules(current, definition, byFilter, env) {
var newFilters = definition.filters,
newRules = definition.rules,
@ -283,16 +276,18 @@ function addRules(current, definition, byFilter, env) {
return current;
}
// Apply inherited styles from their ancestors to them.
//
// called either once per render (in the case of mss) or per layer
// (for mml)
//
// @param {Object} definitions: a list of definitions objects that contain .rules
// @param {Object} env the environment
//
// result: an array of arrays is returned, in which each array refers to a
// specific attachment
/**
* Apply inherited styles from their ancestors to them.
*
* called either once per render (in the case of mss) or per layer
* (for mml)
*
* @param {Object} definitions - a list of definitions objects
* that contain .rules
* @param {Object} env - the environment
* @return {Array<Array>} an array of arrays is returned,
* in which each array refers to a specific attachment
*/
function inheritDefinitions(definitions, env) {
var inheritTime = +new Date();
// definitions are ordered by specificity,
@ -364,16 +359,18 @@ function sortStyles(styles, env) {
return result;
}
// Find a rule like Map { background-color: #fff; },
// if any, and return a list of properties to be inserted
// into the <Map element of the resulting XML. Translates
// properties of the mml object at `m` directly into XML
// properties.
//
// - @param {Object} m the mml object.
// - @param {Array} definitions the output of toList.
// - @param {Object} env.
// - @return {String} rendered properties.
/**
* Find a rule like Map { background-color: #fff; },
* if any, and return a list of properties to be inserted
* into the <Map element of the resulting XML. Translates
* properties of the mml object at `m` directly into XML
* properties.
*
* @param {Object} m the mml object.
* @param {Array} definitions the output of toList.
* @param {Object} env
* @return {String} rendered properties.
*/
function getMapProperties(m, definitions, env) {
var rules = {};
var symbolizers = carto.tree.Reference.data.symbolizers.map;

302
lib/carto/renderer_js.js Normal file
View File

@ -0,0 +1,302 @@
(function(carto) {
var tree = require('./tree');
var _ = global._ || require('underscore');
function CartoCSS(style, options) {
this.options = options || {};
this.imageURLs = [];
if(style) {
this.setStyle(style);
}
}
CartoCSS.Layer = function(shader, options) {
this.options = options;
this.shader = shader;
};
CartoCSS.Layer.prototype = {
fullName: function() {
return this.shader.attachment;
},
name: function() {
return this.fullName().split('::')[0];
},
// frames this layer need to be rendered
frames: function() {
return this.shader.frames;
},
attachment: function() {
return this.fullName().split('::')[1];
},
eval: function(prop) {
var p = this.shader[prop];
if (!p || !p.style) return;
return p.style({}, { zoom: 0, 'frame-offset': 0 });
},
/*
* `props`: feature properties
* `context`: rendering properties, i.e zoom
*/
getStyle: function(props, context) {
var style = {};
for(var i in this.shader) {
if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
style[i] = this.shader[i].style(props, context);
}
}
return style;
},
/**
* return the symbolizers that need to be rendered with
* this style. The order is the rendering order.
* @returns a list with 3 possible values 'line', 'marker', 'polygon'
*/
getSymbolizers: function() {
return this.shader.symbolizers;
},
/**
* returns if the style varies with some feature property.
* Useful to optimize rendering
*/
isVariable: function() {
for(var i in this.shader) {
if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
if (!this.shader[i].constant) {
return true;
}
}
}
return false;
},
getShader: function() {
return this.shader;
},
/**
* returns true if a feature needs to be rendered
*/
filter: function(featureType, props, context) {
for(var i in this.shader) {
var s = this.shader[i](props, context);
if(s) {
return true;
}
}
return false;
},
//
// given a geoemtry type returns the transformed one acording the CartoCSS
// For points there are two kind of types: point and sprite, the first one
// is a circle, second one is an image sprite
//
// the other geometry types are the same than geojson (polygon, linestring...)
//
transformGeometry: function(type) {
return type;
},
transformGeometries: function(geojson) {
return geojson;
}
};
CartoCSS.prototype = {
setStyle: function(style) {
var layers = this.parse(style);
if(!layers) {
throw new Error(this.parse_env.errors);
}
this.layers = layers.map(function(shader) {
return new CartoCSS.Layer(shader);
});
},
getLayers: function() {
return this.layers;
},
getDefault: function() {
return this.findLayer({ attachment: '__default__' });
},
findLayer: function(where) {
return _.find(this.layers, function(value) {
for (var key in where) {
var v = value[key];
if (typeof(v) === 'function') {
v = v.call(value);
}
if (where[key] !== v) return false;
}
return true;
});
},
_createFn: function(ops) {
var body = ops.join('\n');
if(this.options.debug) console.log(body);
return Function("data","ctx", "var _value = null; " + body + "; return _value; ");
},
_compile: function(shader) {
if(typeof shader === 'string') {
shader = eval("(function() { return " + shader +"; })()");
}
this.shader_src = shader;
for(var attr in shader) {
var c = mapper[attr];
if(c) {
this.compiled[c] = eval("(function() { return shader[attr]; })();");
}
}
},
getImageURLs: function(){
return this.imageURLs;
},
parse: function(cartocss) {
var parse_env = {
frames: [],
errors: [],
error: function(obj) {
this.errors.push(obj);
}
};
this.parse_env = parse_env;
var ruleset = null;
try {
ruleset = (new carto.Parser(parse_env)).parse(cartocss);
} catch(e) {
// add the style.mss string to match the response from the server
parse_env.errors.push(e.message);
return;
}
if(ruleset) {
function defKey(def) {
return def.elements[0] + "::" + def.attachment;
}
var defs = ruleset.toList(parse_env);
defs.reverse();
// group by elements[0].value::attachment
var layers = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
var key = defKey(def);
var layer = layers[key] = (layers[key] || {
symbolizers: []
});
for(var u = 0; u<def.rules.length; u++){
var rule = def.rules[u];
if(rule.name === "marker-file" || rule.name === "point-file"){
var value = rule.value.value[0].value[0].value.value;
this.imageURLs.push(value);
}
}
layer.frames = [];
layer.zoom = tree.Zoom.all;
var props = def.toJS(parse_env);
if (this.options.debug) console.log("props", props);
for(var v in props) {
var lyr = layer[v] = layer[v] || {
constant: false,
symbolizer: null,
js: [],
index: 0
};
// build javascript statements
lyr.js.push(props[v].map(function(a) { return a.js; }).join('\n'));
// get symbolizer for prop
lyr.symbolizer = _.first(props[v].map(function(a) { return a.symbolizer; }));
// serach the max index to know rendering order
lyr.index = _.max(props[v].map(function(a) { return a.index; }).concat(lyr.index));
lyr.constant = !_.any(props[v].map(function(a) { return !a.constant; }));
// True when the property is filtered.
lyr.filtered = props[v][0].filtered;
}
}
var ordered_layers = [];
if (this.options.debug) console.log(layers);
var done = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
if (this.options.strict) {
def.toXML(parse_env, {});
if (parse_env.errors.message) {
throw new Error(parse_env.errors.message);
}
}
var k = defKey(def);
var layer = layers[k];
if(!done[k]) {
if(this.options.debug) console.log("**", k);
for(var prop in layer) {
if (prop !== 'zoom' && prop !== 'frames' && prop !== 'symbolizers') {
if(this.options.debug) console.log("*", prop);
layer[prop].style = this._createFn(layer[prop].js);
layer.symbolizers.push(layer[prop].symbolizer);
layer.symbolizers = _.uniq(layer.symbolizers);
}
}
layer.attachment = k;
ordered_layers.push(layer);
done[k] = true;
}
layer.zoom |= def.zoom;
layer.frames.push(def.frame_offset);
}
// uniq the frames
for(i = 0; i < ordered_layers.length; ++i) {
ordered_layers[i].frames = _.uniq(ordered_layers[i].frames);
}
return ordered_layers;
}
return null;
}
};
carto.RendererJS = function (options) {
this.options = options || {};
this.options.mapnik_version = this.options.mapnik_version || 'latest';
this.reference = this.options.reference || require('./torque-reference').version.latest;
this.options.strict = this.options.hasOwnProperty('strict') ? this.options.strict : false;
};
// Prepare a javascript object which contains the layers
carto.RendererJS.prototype.render = function render(cartocss, callback) {
tree.Reference.setData(this.reference);
return new CartoCSS(cartocss, this.options);
}
if(typeof(module) !== 'undefined') {
module.exports = carto.RendererJS;
}
})(require('../carto'));

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,11 @@
/**
* TODO: document this. What does this do?
*/
module.exports.find = function (obj, fun) {
for (var i = 0, r; i < obj.length; i++) {
if (r = fun.call(obj, obj[i])) { return r; }
}
return null;
};
if(typeof(module) !== "undefined") {
module.exports.find = function (obj, fun) {
for (var i = 0, r; i < obj.length; i++) {
if (r = fun.call(obj, obj[i])) { return r; }
}
return null;
};
}

View File

@ -1,5 +1,5 @@
(function(tree) {
var _ = require('underscore');
var _ = global._ || require('underscore');
tree.Call = function Call(name, args, index) {
this.name = name;
this.args = args;
@ -78,6 +78,7 @@ tree.Call.prototype = {
};
}
if (fn !== args.length &&
!(Array.isArray(fn) && _.include(fn, args.length)) &&
// support variable-arg functions like `colorize-alpha`
fn !== -1) {
env.error({

View File

@ -1,6 +1,6 @@
(function(tree) {
var assert = require('assert'),
_ = require('underscore');
_ = global._ || require('underscore');
// A definition is the combination of a selector and rules, like
// #foo {
@ -20,6 +20,7 @@ tree.Definition = function Definition(selector, rules) {
}
this.filters = selector.filters;
this.zoom = selector.zoom;
this.frame_offset = selector.frame_offset;
this.attachment = selector.attachment || '__default__';
this.specificity = selector.specificity();
};
@ -97,6 +98,7 @@ tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
}
sym_order = symbolizerList(sym_order);
var sym_count = 0;
for (var i = 0; i < sym_order.length; i++) {
var attributes = symbolizers[sym_order[i]];
@ -105,6 +107,7 @@ tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
// Skip the magical * symbolizer which is used for universal properties
// which are bubbled up to Style elements intead of Symbolizer elements.
if (symbolizer === '*') continue;
sym_count++;
var fail = tree.Reference.requiredProperties(symbolizer, attributes);
if (fail) {
@ -147,7 +150,7 @@ tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
}
}
}
if (!xml) return '';
if (!sym_count || !xml) return '';
return ' <Rule>\n' + xml + ' </Rule>\n';
};
@ -205,4 +208,41 @@ tree.Definition.prototype.toXML = function(env, existing) {
return xml;
};
tree.Definition.prototype.toJS = function(env) {
var shaderAttrs = {};
var frame_offset = this.frame_offset;
var zoomFilter = "(" + this.zoom + " & (1 << ctx.zoom))";
var filters = [zoomFilter];
var originalFilters = this.filters.toJS(env);
// Ignore default zoom for filtering (https://github.com/CartoDB/carto/issues/40)
var zoomFiltered = this.zoom !== tree.Zoom.all;
if (originalFilters) {
filters.push(originalFilters);
}
if (frame_offset) {
filters.push('ctx["frame-offset"] === ' + frame_offset);
}
_.each(this.rules, function (rule) {
var exportedRule = {};
if (!rule instanceof tree.Rule) {
throw new Error("Ruleset not supported");
}
exportedRule.index = rule.index;
exportedRule.symbolizer = rule.symbolizer;
exportedRule.js = "if(" + filters.join(" && ") + "){" + rule.value.toJS(env) + "}";
exportedRule.constant = rule.value.ev(env).is !== 'field';
exportedRule.filtered = zoomFiltered || (originalFilters !== '');
shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
shaderAttrs[rule.name].push(exportedRule);
});
return shaderAttrs;
};
})(require('../tree'));

View File

@ -1,5 +1,5 @@
(function(tree) {
var _ = require('underscore');
var _ = global._ || require('underscore');
//
// A number with a unit
//
@ -47,6 +47,10 @@ tree.Dimension.prototype = {
return this;
},
round: function() {
this.value = Math.round(this.value);
return this;
},
toColor: function() {
return new tree.Color([this.value, this.value, this.value]);
},

View File

@ -1,4 +1,5 @@
var tree = require('../tree');
var _ = global._ || require('underscore');
tree.Filterset = function Filterset() {
this.filters = {};
@ -77,6 +78,27 @@ tree.Filterset.prototype.cloneWith = function(other) {
return clone;
};
tree.Filterset.prototype.toJS = function(env) {
var opMap = {
'=': '==='
};
return _.map(this.filters, function(filter) {
var op = filter.op;
if(op in opMap) {
op = opMap[op];
}
var val = filter.val;
if(filter._val !== undefined) {
val = filter._val.toString(true);
}
var attrs = "data";
if (op === '=~') {
return "(" + attrs + "['" + filter.key.value + "'] + '').match(" + (val.is === 'string' ? "'" + val.toString().replace(/'/g, "\\'").replace(/&amp;/g, '&') + "'" : val) + ")";
}
return attrs + "['" + filter.key.value + "'] " + op + " " + (val.is === 'string' ? "'" + val.toString().replace(/'/g, "\\'").replace(/&amp;/g, '&') + "'" : val);
}).join(' && ');
};
// Returns true when the new filter can be added, false otherwise.
// It can also return null, and on the other side we test for === true or
// false

View File

@ -0,0 +1,27 @@
var tree = require('../tree');
// Storage for Frame offset value
// and stores them as bit-sequences so that they can be combined,
// inverted, and compared quickly.
tree.FrameOffset = function(op, value, index) {
value = parseInt(value, 10);
if (value > tree.FrameOffset.max || value <= 0) {
throw {
message: 'Only frame-offset levels between 1 and ' +
tree.FrameOffset.max + ' supported.',
index: index
};
}
if (op !== '=') {
throw {
message: 'only = operator is supported for frame-offset',
index: index
};
}
return value;
};
tree.FrameOffset.max = 32;
tree.FrameOffset.none = 0;

View File

@ -8,13 +8,14 @@ tree.Quoted.prototype = {
is: 'string',
toString: function(quotes) {
var xmlvalue = this.value
var escapedValue = this.value
.replace(/&/g, '&amp;')
.replace(/\'/g, '&apos;')
var xmlvalue = escapedValue
.replace(/\'/g, '\\\'')
.replace(/\"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/\>/g, '&gt;');
return (quotes === true) ? "'" + xmlvalue + "'" : this.value;
return (quotes === true) ? "'" + xmlvalue + "'" : escapedValue;
},
'ev': function() {

View File

@ -4,18 +4,26 @@
// combinations.
(function(tree) {
var _ = require('underscore'),
mapnik_reference = require('mapnik-reference'),
var _ = global._ || require('underscore'),
ref = {};
ref.setData = function(data) {
ref.data = data;
ref.selector_cache = generateSelectorCache(data);
ref.mapnikFunctions = generateMapnikFunctions(data);
ref.mapnikFunctions.matrix = [6];
ref.mapnikFunctions.translate = [1, 2];
ref.mapnikFunctions.scale = [1, 2];
ref.mapnikFunctions.rotate = [1, 3];
ref.mapnikFunctions.skewX = [1];
ref.mapnikFunctions.skewY = [1];
ref.required_cache = generateRequiredProperties(data);
};
ref.setVersion = function(version) {
var mapnik_reference = require('mapnik-reference');
if (mapnik_reference.version.hasOwnProperty(version)) {
ref.setData(mapnik_reference.version[version]);
return true;
@ -178,8 +186,6 @@ ref.validValue = function(env, selector, value) {
// For backwards compatibility, you can specify a string for `functions`-compatible
// values, though they will not be validated.
return validateFunctions(value, selector);
} else if (ref.selector(selector).type === 'expression') {
return true;
} else if (ref.selector(selector).type === 'unsigned') {
if (value.value[0].is === 'float') {
value.value[0].round();
@ -187,6 +193,8 @@ ref.validValue = function(env, selector, value) {
} else {
return false;
}
} else if ((ref.selector(selector).expression)) {
return true;
} else {
if (ref.selector(selector).validate) {
var valid = false;
@ -206,8 +214,6 @@ ref.validValue = function(env, selector, value) {
}
};
ref.setVersion('latest');
tree.Reference = ref;
})(require('../tree'));

View File

@ -66,7 +66,7 @@ tree.Ruleset.prototype = {
if (match) {
if (selector.elements.length > 1) {
Array.prototype.push.apply(rules, rule.find(
new tree.Selector(null, null, selector.elements.slice(1)), self));
new tree.Selector(null, null, null, selector.elements.slice(1)), self));
} else {
rules.push(rule);
}
@ -114,6 +114,7 @@ tree.Ruleset.prototype = {
// filters. This means that we only have to clone when
// the zoom levels or the attachment is different too.
if (parent.zoom === (parent.zoom & child.zoom) &&
parent.frame_offset === child.frame_offset &&
parent.attachment === child.attachment &&
parent.elements.join() === child.elements.join()) {
selectors.push(parent);
@ -130,6 +131,7 @@ tree.Ruleset.prototype = {
var clone = Object.create(tree.Selector.prototype);
clone.filters = mergedFilters;
clone.zoom = parent.zoom & child.zoom;
clone.frame_offset = child.frame_offset;
clone.elements = parent.elements.concat(child.elements);
if (parent.attachment && child.attachment) {
clone.attachment = parent.attachment + '/' + child.attachment;

View File

@ -1,9 +1,10 @@
(function(tree) {
tree.Selector = function Selector(filters, zoom, elements, attachment, conditions, index) {
tree.Selector = function Selector(filters, zoom, frame_offset, elements, attachment, conditions, index) {
this.elements = elements || [];
this.attachment = attachment;
this.filters = filters || {};
this.frame_offset = frame_offset;
this.zoom = typeof zoom !== 'undefined' ? zoom : tree.Zoom.all;
this.conditions = conditions;
this.index = index;

View File

@ -1,17 +1,20 @@
(function(tree) {
var _ = require('underscore');
var _ = global._ || require('underscore');
// Given a style's name, attachment, definitions, and an environment object,
// return a stringified style for Mapnik
tree.StyleXML = function(name, attachment, definitions, env) {
var existing = {};
var image_filters = [], direct_image_filters = [], comp_op = [], opacity = [];
var image_filters = [], image_filters_inflate = [], direct_image_filters = [], comp_op = [], opacity = [];
for (var i = 0; i < definitions.length; i++) {
for (var j = 0; j < definitions[i].rules.length; j++) {
if (definitions[i].rules[j].name === 'image-filters') {
image_filters.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'image-filters-inflate') {
image_filters_inflate.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'direct-image-filters') {
direct_image_filters.push(definitions[i].rules[j]);
}
@ -38,6 +41,10 @@ tree.StyleXML = function(name, attachment, definitions, env) {
}).value().join(',') + '"';
}
if (image_filters_inflate.length) {
attrs_xml += ' image-filters-inflate="' + image_filters_inflate[0].value.ev(env).toString() + '"';
}
if (direct_image_filters.length) {
attrs_xml += ' direct-image-filters="' + _.chain(direct_image_filters)
// prevent identical filters from being duplicated in the style
@ -46,11 +53,11 @@ tree.StyleXML = function(name, attachment, definitions, env) {
}).value().join(',') + '"';
}
if (comp_op.length) {
if (comp_op.length && comp_op[0].value.ev(env).value != 'src-over') {
attrs_xml += ' comp-op="' + comp_op[0].value.ev(env).toString() + '"';
}
if (opacity.length) {
if (opacity.length && opacity[0].value.ev(env).value != 1) {
attrs_xml += ' opacity="' + opacity[0].value.ev(env).toString() + '"';
}
var rule_string = rules.join('');

View File

@ -26,7 +26,34 @@ tree.Value.prototype = {
else obj.value = this.value;
obj.is = this.is;
return obj;
},
toJS: function(env) {
//var v = this.value[0].value[0];
var val = this.ev(env);
var v = val.toString();
if(val.is === "color" || val.is === 'uri' || val.is === 'string' || val.is === 'keyword') {
v = "'" + v.replace(/&amp;/g, '&') + "'";
} else if (Array.isArray(this.value) && this.value.length > 1) {
// This covers something like `line-dasharray: 5, 10;`
// where the return _value has more than one element.
// Without this the generated code will look like:
// _value = 5, 10; which will ignore the 10.
v = '[' + this.value.join(',') + ']';
} else if (val.is === 'field') {
// replace [variable] by ctx['variable']
v = v.replace(/\[([^\]]*)\]/g, function(matched) {
return matched.replace(/\[(.*)\]/g, "data['$1']");
});
}else if (val.is === 'call') {
v = JSON.stringify({
name: val.name,
args: val.args
})
}
return "_value = " + v + ";";
}
};
})(require('../tree'));

2110
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,17 @@
{
"name": "carto",
"version": "0.9.5",
"description": "Mapnik Stylesheet Compiler",
"url": "https://github.com/mapbox/carto",
"version": "0.15.1-cdb5",
"description": "CartoCSS Stylesheet Compiler",
"url": "https://github.com/cartodb/carto",
"repository": {
"type": "git",
"url": "http://github.com/mapbox/carto.git"
"type": "git",
"url": "http://github.com/cartodb/carto.git"
},
"author": {
"name": "MapBox",
"url": "http://mapbox.com/",
"email": "info@mapbox.com"
"name": "CartoDB",
"url": "http://cartodb.com/"
},
"keywords": [
"mapnik",
"maps",
"css",
"stylesheets"
@ -21,14 +19,17 @@
"contributors": [
"Tom MacWright <macwright@gmail.com>",
"Konstantin Käfer",
"Alexis Sellier <self@cloudhead.net>"
"Alexis Sellier <self@cloudhead.net>",
"Raul Ochoa <rochoa@cartodb.com>",
"Javi Santana <jsantana@cartodb.com>"
],
"licenses": [{
"licenses": [
{
"type": "Apache"
}],
}
],
"bin": {
"carto": "./bin/carto",
"mml2json.js": "./bin/mml2json.js"
"carto": "./bin/carto"
},
"man": "./man/carto.1",
"main": "./lib/carto/index",
@ -36,18 +37,27 @@
"node": ">=0.4.x"
},
"dependencies": {
"underscore": "~1.4.3",
"mapnik-reference": "~5.0.7",
"xml2js": "~0.2.4",
"underscore": "1.8.3",
"mapnik-reference": "~6.0.2",
"optimist": "~0.6.0"
},
"devDependencies": {
"mocha": "1.12.x",
"jshint": "0.2.x",
"sax": "0.1.x"
"sax": "0.1.x",
"istanbul": "~0.2.14",
"coveralls": "~2.10.1",
"browserify": "~7.0.0",
"uglify-js": "1.3.3"
},
"scripts": {
"pretest": "npm install",
"test": "mocha -R spec"
"test": "mocha -R spec",
"tdd": "env HIDE_LOGS=true mocha -w -R spec",
"coverage": "istanbul cover ./node_modules/.bin/_mocha && coveralls < ./coverage/lcov.info",
"bump": "npm version patch",
"bump:major": "npm version major",
"bump:minor": "npm version minor",
"postversion": "git push origin master --follow-tags"
}
}

37
test/bincarto.test.js Normal file
View File

@ -0,0 +1,37 @@
var assert = require('assert');
var exec = require('child_process').exec;
var path = require('path');
var util = require('util');
var helper = require('./support/helper');
var bin = path.resolve(path.join(__dirname, '..', 'bin', 'carto'));
var fs = require('fs');
describe('bin/carto', function() {
it('errors on no input', function(done) {
exec(bin, function(err, stdout, stderr) {
assert.equal(1, err.code);
assert.equal("carto: no input files ('carto -h or --help' for help)\n", stdout);
done();
});
});
it('renders mml', function(done) {
var file = path.join(__dirname, 'rendering', 'identity.mml');
exec(util.format('%s %s', bin, file), function(err, stdout, stderr) {
assert.ifError(err);
helper.compareToXMLFile(helper.resultFile(file), stdout, done, [
helper.removeAbsoluteImages,
helper.removeAbsoluteDatasources
]);
});
});
it('renders mss', function(done) {
var file = path.join(__dirname, 'rendering-mss', 'empty_name.mss');
exec(util.format('%s %s', bin, file), function(err, stdout, stderr) {
assert.ifError(err);
var expected = file.replace(path.extname(file),'')+'.xml';
var expected_data = fs.readFileSync(expected, 'utf8');
assert.equal(stdout,expected_data + '\n');
done();
});
});
});

25
test/color.test.js Normal file
View File

@ -0,0 +1,25 @@
var assert = require('assert');
var tree = require('../lib/carto/tree.js');
require('../lib/carto/functions');
require('../lib/carto/tree/color');
require('../lib/carto/tree/dimension');
describe('Color', function() {
describe('basic functionality', function() {
it('should be constructed', function() {
var f = new tree.Color([0, 0, 0], 1);
assert.deepEqual(f.toHSL(), {"h":0,"s":0,"l":0,"a":1});
assert.ok(f);
});
});
describe('functions', function() {
it('should be constructed', function() {
assert.deepEqual(tree.functions.rgb(0, 0, 0), new tree.Color([0, 0, 0], 1));
assert.deepEqual(tree.functions.hue(new tree.Color([0, 0, 0], 1)), new tree.Dimension(0));
assert.deepEqual(tree.functions.saturation(new tree.Color([0, 0, 0], 1)), new tree.Dimension(0, '%'));
assert.deepEqual(tree.functions.lightness(new tree.Color([0, 0, 0], 1)), new tree.Dimension(0, '%'));
assert.deepEqual(tree.functions.alpha(new tree.Color([0, 0, 0], 1)), new tree.Dimension(1));
assert.deepEqual(tree.functions.greyscale(new tree.Color([0, 0, 0], 1)), new tree.Color([0, 0, 0], 1));
});
});
});

14
test/comment.test.js Normal file
View File

@ -0,0 +1,14 @@
var assert = require('assert');
var tree = require('../lib/carto/tree.js');
require('../lib/carto/tree/comment');
describe('Comment', function() {
describe('basic functionality', function() {
it('should be constructed', function() {
var f = new tree.Comment('hello world');
assert.deepEqual(f.toString(), '<!--hello world-->');
assert.deepEqual(f.ev(), f);
assert.ok(f);
});
});
});

View File

@ -19,29 +19,20 @@ helper.files('errorhandling', 'mml', function(file) {
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
filename: file
}).render(mml, function (err) {
if (!err) {
throw new Error("*** invalid error handling test found: " + basename + ": all error handling tests should throw!");
}
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
if (!err) assert.deepEqual(output, data.substr(0, data.length - 1));
done();
});
});
}).render(mml);
// should not get here
assert.ok(false);
done();
} catch(err) {
if (err.message.indexOf('***') > -1) throw err;
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
if (!err) assert.deepEqual(output, data.substr(0, data.length - 1));
done();
});
// fs.writeFileSync(helper.resultFile(file), output);
var data = fs.readFileSync(helper.resultFile(file), 'utf8');
assert.deepEqual(output, data);
done();
}
});
});
@ -65,29 +56,20 @@ helper.files('errorhandling', 'mss', function(file) {
// note: we use the basename here so that the expected error result
// will match if the style was loaded from mml
filename: basename
}).renderMSS(mss, function (err) {
if (!err) {
throw new Error("*** invalid error handling test found: " + basename + ": all error handling tests should throw!");
}
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
if (!err) assert.deepEqual(output, data.substr(0, data.length - 1));
done();
});
});
}).renderMSS(mss);
// should not get here
assert.ok(false);
done();
} catch(err) {
if (err.message.indexOf('***') > -1) throw err;
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
if (!err) assert.deepEqual(output, data.substr(0, data.length - 1));
done();
});
// fs.writeFileSync(helper.resultFile(file), output);
var data = fs.readFileSync(helper.resultFile(file), 'utf8');
assert.deepEqual(output, data);
done();
}
});
});

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"bad_op.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,3 @@
#world {
line-width: 20% + 2px;
}

View File

@ -0,0 +1 @@
bad_op.mss:2:4 If two operands differ, the first must not be %

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"bad_op_2.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,3 @@
#world {
line-width: 20px * 2%;
}

View File

@ -0,0 +1 @@
bad_op_2.mss:2:4 Percent values can only be added or subtracted from other values

View File

@ -1,5 +1,4 @@
#world[zoom=5] {
text-face-name: 2;
line-rasterizer: 'full';
text-name: 'foo';
}

View File

@ -1,2 +1 @@
invalid_value.mss:2:2 Invalid value for text-face-name, the type font is expected. 2 (of type float) was given.
invalid_value.mss:3:2 Invalid value for line-rasterizer, the type keyword (options: full, fast) is expected. full (of type string) was given.

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"invaliddimension.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,3 @@
#world {
line-width: 10wifflewaffles;
}

View File

@ -0,0 +1 @@
invaliddimension.mss:2:4 Invalid unit: 'wifflewaffles'

View File

@ -0,0 +1 @@
issue119.mss:2:2 Map properties are not permitted in other rules

View File

@ -0,0 +1 @@
issue124.mss:6:0 missing closing `}`

View File

@ -1,4 +1,4 @@
#t {
text-name: invalid;
text-face-name: "Dejagnu";
text-name: valid;
text-face-name: 2;
}

View File

@ -1 +1 @@
issue297.mss:2:2 Invalid value for text-name, the type expression is expected. invalid (of type keyword) was given.
issue297.mss:3:2 Invalid value for text-face-name, the type font is expected. 2 (of type float) was given.

View File

@ -0,0 +1 @@
issue_204_a.mss:3:1 missing opening `{`

View File

@ -0,0 +1 @@
issue_204_b.mss:3:3 missing opening `{`

View File

@ -0,0 +1 @@
issue_204_c.mss:4:0 missing opening `{`

View File

@ -0,0 +1 @@
issue_218.mss:5:2 missing opening `{`

View File

@ -0,0 +1 @@
multi_stylesheets_b.mss:2:2 Unrecognized rule: polygonopacity. Did you mean polygon-opacity?

View File

@ -0,0 +1 @@
multi_stylesheets_b.mss:2:2 Unrecognized rule: polygonopacity. Did you mean polygon-opacity?

View File

@ -0,0 +1 @@
nopound.mss:1:0 Invalid code: world {

View File

@ -0,0 +1 @@
notenoughargs.mss:3:31 incorrect number of arguments for darken(). 2 expected.

102
test/filtered.test.js Normal file
View File

@ -0,0 +1,102 @@
/**
* Test the filtered field.
*
* When compiled, a rule provides metainformation fields like index, constant...etc
* one of this fields is the "filtered field".
*
* This field gives information about whether a property is filtered or not.
*
* A property is filtered if it was activated inside a filter. In the following cartocss
* code marker-color.filtered will be true because it's inside a population filter.
*
* #layer {
* maker-width: 20;
* [population > 100] {
* marker-color: red; // this property is filtered
* }
* }
*
* "zoom" is a special case, and it only should be considered when its value is not the default.
*/
var assert = require('assert');
var Carto = require('../lib/carto/index.js');
var renderer = new Carto.RendererJS({ strict: true });
describe('property.filtered', function () {
it('should be false when the property is not filtered', function () {
var style = [
'#layer {',
' marker-fill: red;',
'}'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(!layers['marker-fill'].filtered);
});
it('should be true when the property is filtered', function () {
var style = [
'#layer {',
' [foo > 30] {',
' marker-fill: red;',
' }',
'}'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
it('should be true when the property is filtered at first level', function () {
var style = [
'#layer [foo > 30] {',
' marker-fill: red;',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
it('should be false when the property is not filterd but there is another filtered properties', function () {
var style = [
'#layer {',
' marker-fill: red;',
' [bar < 200]{',
' marker-allow-overlap: false;',
' }',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(!layers['marker-fill'].filtered);
assert(layers['marker-allow-overlap'].filtered);
});
it('should be true when the property is filtered and have a default value', function () {
var style = [
'#layer {',
' marker-fill: red;',
' [bar < 200]{',
' marker-fill: blue;',
' }',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
it('should be true when filtering by zoom', function () {
var style = [
'#layer {',
' [zoom < 5]{',
' marker-fill: blue;',
' }',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
});

56
test/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Carto Test</title>
<script src='underscore.js' type='text/javascript'></script>
<script src='reference.js' type='text/javascript'></script>
<script src='../dist/carto.js' type='text/javascript'></script>
<script>
function test() {
var style = document.getElementById('style').innerHTML;
var parse_env = {
error: function(obj) {
document.getElementById('error').innerHTML += JSON.stringify(obj);
}
};
var ruleset = (new carto.Parser(parse_env)).parse(style);
var definition = ruleset.toList(parse_env);
console.log(definition);
console.log(window.a = (new carto.RendererJS()).render(style));
// for (var i in ruleset.rules) {
// for (var j in ruleset.rules[i].rules) {
// document.getElementById('output').innerHTML += ruleset.rules[i].rules[j].eval(parse_env).toXML(parse_env) + ' ';
// }
// }
}
</script>
</head>
<body onload='test()'>
<h3>Style</h3>
<pre id='style'>
#world {
line-width: 2;
line-color: #f00;
[frame-offset = 1] {
line-width: 3;
}
[frame-offset = 2] {
line-width: 3;
}
}
#worls[frame-offset = 10] {
line-width: 4;
}
</pre>
<h3>Error</h3>
<pre id='error'>
</pre>
<h3>Output</h3>
<pre id='output'>
</pre>
</body>
</html>

View File

@ -12,12 +12,12 @@ describe('Quoted', function() {
it('should produce normal output', function() {
var f = new tree.Quoted("Tom's & \"<quoted>\"");
assert.ok(f);
assert.equal(f.toString(), "Tom's & \"<quoted>\"");
assert.equal(f.toString(), "Tom's &amp; \"<quoted>\"");
});
it('should produce xml-friendly output', function() {
var f = new tree.Quoted("Tom's & \"<quoted>\"");
assert.ok(f);
assert.equal(f.toString(true), "'Tom&apos;s &amp; &quot;&lt;quoted&gt;&quot;'");
assert.equal(f.toString(true), "'Tom\\'s &amp; &quot;&lt;quoted&gt;&quot;'");
});
});
});

1498
test/reference.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,34 +11,30 @@ var helper = require('./support/helper');
describe('Rendering mss', function() {
helper.files('rendering-mss', 'mss', function(file) {
it('should render mss ' + path.basename(file) + ' correctly', function(done) {
it('should render mss ' + path.basename(file) + ' correctly', function() {
var completed = false;
var renderResult;
var mss = helper.mss(file);
new carto.Renderer({
paths: [ path.dirname(file) ],
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
filename: file
}).renderMSS(mss, function (err, output) {
if (err) {
if (Array.isArray(err)){
err.forEach(carto.writeError);
done();
} else {
throw err;
done();
}
try {
var output = new carto.Renderer({
paths: [ path.dirname(file) ],
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
filename: file
}).renderMSS(mss);
} catch(err) {
if (Array.isArray(err)){
err.forEach(carto.writeError);
} else {
var expected = file.replace(path.extname(file),'')+'.xml';
if (!existsSync(expected)) {
fs.writeFileSync(expected,output);
}
var expected_data = fs.readFileSync(expected).toString();
assert.equal(output,expected_data);
done();
throw err;
}
});
}
var expected = file.replace(path.extname(file),'')+'.xml';
if (!existsSync(expected)) {
fs.writeFileSync(expected,output);
}
var expected_data = fs.readFileSync(expected).toString();
assert.equal(output.trim(),expected_data.trim());
});
});
});

View File

@ -1,4 +1,5 @@
#layer {
image-filters:invert();
direct-image-filters:invert();
image-filters:invert();
image-filters-inflate:true;
direct-image-filters:invert();
}

View File

@ -1,2 +1,2 @@
<Style name="style" filter-mode="first" image-filters="invert" direct-image-filters="invert">
<Style name="style" filter-mode="first" image-filters="invert" image-filters-inflate="true" direct-image-filters="invert">
</Style>

View File

@ -0,0 +1,4 @@
#layer["Hello&Goodbye"="yes"] {
text-name: [name];
text-face-name: "El&Font Bubble Regular";
}

View File

@ -0,0 +1,6 @@
<Style name="style" filter-mode="first">
<Rule>
<Filter>([Hello&amp;Goodbye] = 'yes')</Filter>
<TextSymbolizer face-name="El&amp;Font Bubble Regular" ><![CDATA[[name]]]></TextSymbolizer>
</Rule>
</Style>

View File

@ -0,0 +1,9 @@
#somelayername {
[feature = 'highway_motorway'],
[feature = 'highway_motorway_link'] {
/* code for any motorway */
[feature = 'highway_motorway_link'] {
/* code specific for links */
}
}
}

View File

View File

@ -0,0 +1 @@
#poi_label[maki=''] { opacity:.5; }

View File

@ -0,0 +1,2 @@
<Style name="style" filter-mode="first" opacity="0.5">
</Style>

View File

@ -0,0 +1,4 @@
#poi_label[maki=''] {
opacity:1.0;
comp-op:src-over;
}

View File

View File

@ -0,0 +1,3 @@
#layer {
line-width:"@zoom";
}

View File

@ -0,0 +1,5 @@
<Style name="style" filter-mode="first">
<Rule>
<LineSymbolizer stroke-width="@zoom" />
</Rule>
</Style>

View File

@ -0,0 +1,4 @@
#layer {
text-name: [name];
text-face-name: "El&Font Bubble Regular";
}

View File

@ -0,0 +1,5 @@
<Style name="style" filter-mode="first">
<Rule>
<TextSymbolizer face-name="El&amp;Font Bubble Regular" ><![CDATA[[name]]]></TextSymbolizer>
</Rule>
</Style>

View File

@ -0,0 +1,5 @@
#world {
line-rasterizer: 'fast';
// carto should collapse to one
line-rasterizer: fast;
}

View File

@ -0,0 +1,5 @@
<Style name="style" filter-mode="first">
<Rule>
<LineSymbolizer rasterizer="fast" />
</Rule>
</Style>

View File

@ -0,0 +1,6 @@
<Style name="style" filter-mode="first">
<Rule>
<Filter>([zoomy] &gt; 2)</Filter>
<PolygonSymbolizer fill="#ff0000" />
</Rule>
</Style>

View File

@ -12,52 +12,52 @@ helper.files('rendering', 'mml', function(file) {
var completed = false;
var renderResult;
var mml = helper.mml(file);
new carto.Renderer({
paths: [ path.dirname(file) ],
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
filename: file
}).render(mml, function (err, output) {
if (err) {
if (Array.isArray(err)){
err.forEach(carto.writeError);
} else {
throw err;
}
try {
var output = new carto.Renderer({
paths: [ path.dirname(file) ],
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
filename: file
}).render(mml);
} catch(err) {
if (Array.isArray(err)){
err.forEach(carto.writeError);
return done();
} else {
var result = helper.resultFile(file);
renderResult = output;
helper.compareToXMLFile(result, output, function(err,expected_json,actual_json) {
completed = true;
var actual = file.replace(path.extname(file),'') + '-actual.json';
var expected = file.replace(path.extname(file),'') + '-expected.json';
if (err) {
// disabled since it can break on large diffs
/*
console.warn(
helper.stylize("Failure", 'red') + ': ' +
helper.stylize(file, 'underline') +
' differs from expected result.');
helper.showDifferences(err);
throw '';
*/
fs.writeFileSync(actual,JSON.stringify(actual_json,null,4));
fs.writeFileSync(expected,JSON.stringify(expected_json,null,4));
throw new Error('failed: xml ' + result + ' in json form does not match expected result:\n' + actual + ' (actual)\n' + expected + ' (expected)');
} else {
// cleanup any actual renders that no longer fail
try {
fs.unlinkSync(actual);
fs.unlinkSync(expected);
} catch (err) {}
}
done();
}, [
helper.removeAbsoluteImages,
helper.removeAbsoluteDatasources
]);
return done(err);
}
});
}
var result = helper.resultFile(file);
renderResult = output;
helper.compareToXMLFile(result, output, function(err,expected_json,actual_json) {
completed = true;
var actual = file.replace(path.extname(file),'') + '-actual.json';
var expected = file.replace(path.extname(file),'') + '-expected.json';
if (err) {
// disabled since it can break on large diffs
/*
console.warn(
helper.stylize("Failure", 'red') + ': ' +
helper.stylize(file, 'underline') +
' differs from expected result.');
helper.showDifferences(err);
throw '';
*/
fs.writeFileSync(actual,JSON.stringify(actual_json,null,4));
fs.writeFileSync(expected,JSON.stringify(expected_json,null,4));
throw new Error('failed: xml ' + result + ' in json form does not match expected result:\n' + actual + ' (actual)\n' + expected + ' (expected)');
} else {
// cleanup any actual renders that no longer fail
try {
fs.unlinkSync(actual);
fs.unlinkSync(expected);
} catch (err) {}
}
done();
}, [
helper.removeAbsoluteImages,
helper.removeAbsoluteDatasources
]);
// beforeExit(function() {
// if (!completed && renderResult) {

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<Style name="data" filter-mode="first" >

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" background-color="#ffffff" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" background-color="#ffffff" >
</Map>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" buffer-size="256" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" buffer-size="256">
<FontSet name="fontset-0">
<Font face-name="Georgia Regular"/>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<Style name="world" filter-mode="first" >

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" >
<Style name="world" filter-mode="first">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<Style name="just_land" filter-mode="first" >

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<Style name="world" filter-mode="first">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" >
<FontSet name="fontset-0">
<Font face-name="Georgia Regular"/>

Some files were not shown because too many files have changed in this diff Show More