Importing/Exporting JointJS Graphs with devs.Model Elements - javascript

So I am trying to implement an "Import Graph" feature to my JointJS app. All of the elements I am working with inherit from devs.Model such that,
var myCustomShape = new joint.shapes.devs.Model();
These shapes include custom attributes on top of devs.Model that include sub-objects and function attributes.
Importing my graph with JSON.parse results in Error:
joint.js:13580 Uncaught Error: dia.ElementView: markup required
at child.renderMarkup (joint.js:13580)
at child.render (joint.js:13616)
at child.protoProps.render (joint.js:9692)
at child.renderView (joint.js:17645)
at child.resetViews (joint.js:17691)
at triggerEvents (backbone.js:370)
at triggerApi (backbone.js:356)
at eventsApi (backbone.js:155)
at child.Events.trigger (backbone.js:346)
at triggerEvents (backbone.js:371)
I read on the JointJS docs:
Keep in mind the general limitations of the JSON format. Some commonly used >native JavaScript data types (including Function, Date, and undefined) are >not supported. The variables that have values of these types will not be >persisted. Among other things, this means that if persistence is important in >your application, you should choose to define custom element/link subtypes >instead of embedding custom functions into default joint.dia.Element and >joint.dia.Link types.
Additionally, if you want to make use of the JSON objects and directly store >them into MongoDB, you should remember its additional restriction on object >keys starting with the . (dot) or $ (dollar sign) symbols. Those are reserved >for internal use of the MongoDB system. This is significant in the context of >JointJS because it precludes the use of CSS-style selectors in the attrs >arrays of your Elements and Links. Therefore, if persistence is important to >you and you want to save data directly to MongoDB, you should always define >custom subelement selectors in the markup of your custom elements instead of >relying on CSS-style selectors.
I am wondering if there are any known work arounds here that I can use to save my graph along with all of my custom elements. Please be gentle, this is my first javascript app, first time working with JSON and first time working with JointJS.

I have answered my own question.
If you inherit from a shape without extending it, there is no need for any markup in your declaration of your new shape. Here is the old way of declaring a shape that I was using:
OLD CODE DON'T USE THIS
var myCustomShape = new joint.shapes.devs.Model({
position: { x:50, y:50 },
size: { width: 75, height: 75 },
inPorts: ['Input'],
outPorts: ['Output'],
attrs: {
'.label': {
text: 'Source',
fill: 'black'
},
rect: {
fill: 'springgreen'
}
},
type: 'custom.myCustomShape'
});
So I switched over to extending the devs.Model shape and then make a new of my type like so:
NEW WORKING CODE :)
joint.shapes.custom.myCustomShape = joint.shapes.devs.Model.extend({
markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g><image/><text class="label"/><g class="inPorts"/><g class="outPorts"/></g>',
defaults: joint.util.deepSupplement({
type: 'custom.myCustomShape',
size: { width: 75, height: 75 },
rect: {
stroke: '#d1d1d1',
fill: 'white'
},
circle: {
stroke: 'gray'
},
'.label': {
text: 'Base',
'ref-y': -20
},
'.inPorts circle': {
fill: '#c8c8c8'
},
'.outPorts circle': {
fill: '#262626'
},
}, joint.shapes.devs.Model.prototype.defaults)
});
And is used by:
var customShape= new joint.shapes.custom.myCustomShape({
attrs: {
'.label': {
text: 'My Shape',
fill: 'black'
},
rect: {
fill: 'springgreen'
}
},
position: { x: 50, y: 50 },
size: { width: 75, height: 75 }
});
graph.addCell(customShape);
When exporting and importing graphs with shapes declared like this, everything works properly.

Related

Set custom colour palette in Plotly js in Angular

I am using Plotly js in my recent angular project. I implemented the library successfully by using its angular based wrapper.
There is one thing that I tried to implement in many ways but failed. I am trying to use my custom color palette for its charts.
I find a work around by passing the colour in the chart data layout like,
data = [{
values: allValues[0],
labels: allLabels,
type: 'pie',
name: 'Starry Night',
marker: {
colors: [['rgb(56, 75, 126)', 'rgb(18, 36, 37)', 'rgb(34, 53, 101)', 'rgb(36, 55, 57)', 'rgb(6, 4, 4)']]
},
domain: {
row: 0,
column: 0
},
hoverinfo: 'label+percent+name',
textinfo: 'none'
}
and it worked but it's not the perfect way because I need to add this in every chart data and need to take care of how many data points are there so I push those many color codes.
Is there any way I can provide my color palette in somewhere like config so that every time a chart gets initialize it start taking colors from the custom-defined palette.
The layout property takes a property colorway which takes a list of colour names/codes.
public graph: any = {
data: [],
layout: {
colorway: ["red", "green", "blue", "goldenrod", "magenta"],
autosize: true,
}
}
But I haven't yet figured out how to set this globally.

Changing style of map elements dynamically

I am attempting to change the style of a number of map elements on the fly, without success. By on the fly I mean changing the color of a map element, the water mass for instance, dynamically from a computed color. Using Tangram but open to other engines if there is a better solution. Since Tangram uses a YAML file to style the various elements, I am using inline Javascript on the scene (YAML) file. In other words, instead of giving it a fixed color, I want to compute the color (from an image). Instead of this:
water:
draw:
polygons:
color: "blue"
I am doing something along these lines (further complicated because I am using Vue Router):
water:
draw:
polygons:
color: function() { return this.colors[0]; }
computedColors is computed in a mixing, then broadcasted to the appropriate route:
var colorize = {
data: function () {
return {
computedColors: []
};
},
created: function () {
this.getColors();
},
methods: {
getColors: function () {
...
self.computedColors = [
...
];
self.$broadcast('parent-colors', self.computedColors);
...
};
};
}
Here is the route:
router.map({
'/map': {
name: 'map',
component: Vue.extend({
template: '#map',
mixins: [colorize],
data: function () {
return {
colors: []
};
},
events: {
'parent-colors': function (computedColors) {
this.colors = computedColors;
}
},
ready: {
var map = L.map('map');
var layer = Tangram.leafletLayer({
scene: './tiles/scene.yaml'
});
layer.addTo(map);
map.setView([40.70531887544228, -74.00976419448853], 15);
}
});
Any hints on what I may be doing wrong appreciated.
UPDATE
I am getting an error of sorts. It is related with Tangram but not quite able to figure out what it is exactly. Seems to be an issue with the parsing of the YAML file. If I change this in my scene.yaml:
water:
draw:
polygons:
color: function() { return this.colors[0]; }
With this:
water:
draw:
polygons:
color: function() { return this.color1; }
I get no errors but unfortunately the water mass still isn't assigned any color either.
Of course, I had to change these lines in the map route instance too:
data: function () {
return {
color1: ''
};
},
...
events: {
'parent-colors': function (computedColors) {
this.color1 = computedColors[0];
}
}
The following doesn't offer a solution to the specific problem of styling a Tangram map on the fly (which Yaml doesn't seem to easily allow), but it partially answers the question of how to style map vectors dynamically. It implements the plugin Leaflet.VectorGrid and assigns properties to layers programmatically through the vectorTileLayerStyles method (accomplished in the example below with color: self.colors[6]).
L.vectorGrid.slicer(countries, {
rendererFactory: L.svg.tile,
vectorTileLayerStyles: {
sliced: function() {
return {
stroke: true,
color: self.colors[6],
weight: 0.5,
};
}
},
}).addTo(map);
The variable countries is really just a GeoJson with var countries = added to it, something along these lines:
var countries = {
"type": "FeatureCollection",
"features": [
{ "type": "Feature"
(...)
}
]
};
It's a straightforward solution and it works perfectly with small amounts of data, but since it's a client-side solution it's heavy on the browser when dealing with bigger data sets. Still, it may be useful for those who may be looking for a simple way to style a simplified world map or a limited map area on the fly.
UPDATE
A more performant solution is to use a tile server. The example below implements t-rex, with canvas renderer L.canvas.tile in the rendererFactory option instead of L.svg.tile and protobuf:
var lines = "http://127.0.0.1:6767/lines/{z}/{x}/{y}.pbf";
var multilinestrings = "http://127.0.0.1:6767/multilinestrings/{z}/{x}/{y}.pbf";
var multipolygons = "http://127.0.0.1:6767/multipolygons/{z}/{x}/{y}.pbf";
var vectorTileStyling = {
lines: {
weight: .8,
fillColor: this.colors[1],
color: this.colors[1],
fillOpacity: .8,
opacity: .8
},
multilinestrings: {
weight: .8,
fillColor: this.colors[2],
color: this.colors[2],
fillOpacity: .8,
opacity: .8
},
multipolygons: {
fill: true,
weight: .8,
fillColor: this.colors[3],
color: this.colors[3],
fillOpacity: .8,
opacity: .8,
}
};
var externalVectorTileOptions = {
rendererFactory: L.canvas.tile,
vectorTileLayerStyles: vectorTileStyling,
maxZoom: 16,
minZoom: 14
};
L.vectorGrid.protobuf(lines, externalVectorTileOptions).addTo(map);
L.vectorGrid.protobuf(multilinestrings, externalVectorTileOptions).addTo(map);
L.vectorGrid.protobuf(multipolygons, externalVectorTileOptions).addTo(map);

How to make Patternizer pattern larger without losing quality

So I recently found a neat tool called Patternizer (maybe you've heard of it) which has an easy to use interface that creates patterns, which you can then instantly get the code for and use along with their js library.
So to properly use the library and the given code it requires a canvas element, so to make things easy I took their example element and id: <canvas id="bgCanvas"></canvas> and put that right after my header end tag.
The javascript for the simple pattern I created is:
bgCanvas.patternizer({
stripes : [
{
color: '#000000',
rotation: 315,
opacity: 50,
mode: 'normal',
width: 3,
gap: 98,
offset: 123
},
{
color: '#000000',
rotation: 45,
opacity: 50,
mode: 'normal',
width: 3,
gap: 98,
offset: 123
},
{
color: '#0d050a',
rotation: 25,
opacity: 60,
mode: 'normal',
width: 100,
gap: 100,
offset: 156
},
{
color: '#0d050a',
rotation: 335,
opacity: 60,
mode: 'normal',
width: 100,
gap: 100,
offset: 156
}
],
bg : '#231d1d'
});
Now. It works, however I noticed the canvas element is extremely small:
As you can see, the canvas (in the top left) is much too small. So my first instinct was to change it's CSS to: canvas {width: 100%; height: 100%; to which I further added: z-index: -1;position:absolute;}
however heres what I got next:
As you can see the canvas is much bigger, however, the pattern has simply blown up and loses it's previous scale and quality. Is there a simple fix to this in CSS I'm missing? Please help!
Thanks,
-- micoxion
I think you need to specify the canvas height and width in the html of the element like stated on the patternizer docs.
https://info.patternizer.com/docs/
If what you need is a nice simple pattern i would recommend using a CSS only solution like the one found on http://lea.verou.me/css3patterns/
Using js and a canvas for this is too much trouble.
EDIT: If you are using those patterns i recommend passing the code through https://autoprefixer.github.io so it has the correct prefixes and works in all browsers.
You can learn more about css patterns and techniques at https://css-tricks.com/stripes-css/

JVectorMap Multimap drill down has different style when drilled down, how can I change this style as well?

This question was asked here as well maybe a year ago, but no answer was provided. I'd like to know how I can make the "drilled-down" map have the same style and background as my main map.
var regionStyling9 = { // This style is set to the main map's regionStyle
initial: {
fill: '#E9E9E9',
stroke: '#505050',
"fill-opacity": 1,
"stroke-width": 1,
"stroke-opacity": 1
},
hover: { fill: "#4DB870" }
};
$(function() {
new jvm.MultiMap({
container: $('#Chart9Map'),
maxLevel: 1,
main: {
map: 'us_aea_en',
backgroundColor: '#fff',
regionStyle: regionStyling9, // Here I set the top level style to the main map
series: {
regions: [{ values: stateMapData, scale: ['#cce2ec', '#006593'], normalizeFunction: 'polynomial' }]
}
},
mapUrlByCode: function(code, multiMap) {
return '../Scripts/JVectorMap/jquery-jvectormap-data-' +
code.toLowerCase() +
'-' +
multiMap.defaultProjection +
'-en.js';
}
});
});
How can I affect the style of the secondary chart like I did for the main chart?
According to the jVectorMap MultiMap Documentation, there is only config for the main map, no other object for the drill down maps...
I know what you mean, I also could not find this info. It is very strange the library doesnt handle this. Its such a well produced package, yet seems to me missing some basics.
Assuming you arent using the free version, the license says you are allowed to modify the code.
Open up the jquery-jvectormap.js file (prettify it is needed).
At the bottom you'll see the lines:
currentMap.params.container.hide(), that.maps[name] ? that.maps[name].params.container.show() : that.addMap(name, {
map: name,
multiMapLevel: currentMap.params.multiMapLevel + 1
}), that.history.push(that.maps[name]), that.backButton.show()
Change this so settings from the main map are passed down to the children (so just add the 3 extra lines):
currentMap.params.container.hide(), that.maps[name] ? that.maps[name].params.container.show() : that.addMap(name, {
backgroundColor: that.params.main.backgroundColor,
series: that.params.main.series,
onRegionTipShow: that.params.main.onRegionTipShow,
map: name,
multiMapLevel: currentMap.params.multiMapLevel + 1

Display multiple rickshaw graphs on the same html page

I currently have a couple of dynamically generated JS based Rickshaw graphs similar to http://code.shutterstock.com/rickshaw/examples/extensions.html
I am kinda new to JS, html etc. How do I go about displaying more than one rickshaw graph on the same html page?
NOTE: I am NOT asking about showing multiple trends on the same graph.
let's say you have the following graph:
var graph = new Rickshaw.Graph({
series: [ { data: [ { x: 0, y: 2 }, { x: 1, y: 4 } ...
renderer: 'area',
element: document.querySelector('#graph')
});
graph.render();
what you've just done is create a global variable called graph that is an instance of the Rickshaw class*, which renders content to the HTML element whose id is graph (ie: <div id="graph"></div>).
To render another graph, create a new instance:
var graph2 = new Rickshaw.Graph({
series: [ { data: [ { x: 0, y: 5 }, { x: 1, y: 9 } ...
renderer: 'area',
element: document.querySelector('#graph2')
});
graph2.render();
... and bind it to a new element:
<div id="graph2">
*Although Javascript has prototypes rather than classes, it is helpful to think of functions in classical terms sometimes when you are invoking them with the new keyword.

Categories

Resources