Cesium GeoJSON datasource polygon material is white despite an image is specified - cesiumjs

I am not able to figure out a way to change the material of a polygon entity from a GeoJsonDataSource. I would like to apply an image.
Here is an example using a color because I don't know how to embed an image on the online sandcastle:
var viewer = new Cesium.Viewer("cesiumContainer");
const poly = {
"type": "FeatureCollection",
"name": "MyPolygon",
"crs": {"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}},
"features": [
{"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[[ 10.746500009923748, 48.314700009648320, 500 ],
[ 10.747500009924019, 48.315700009648104, 500 ],
[ 10.747038310965864, 48.315905422444722, 550 ],
[ 10.746038315853207, 48.314905418639555, 550 ],
[ 10.746500009923748, 48.314700009648320, 500 ]]
]}}]};
const Promise0 = async () => {
try {
const dataSource = await Cesium.GeoJsonDataSource.load(poly, {
stroke: Cesium.Color.BLUE,
strokeWidth: 3
});
const Promise1 = async () => {
try {
const polygonalFrame = await viewer.dataSources.add(dataSource);
viewer.zoomTo(polygonalFrame);
const entities = polygonalFrame.entities.values;
for (var i = 0; i < entities.length; i++) {
const entity = entities[i];
entity.polygon.material = new Cesium.Material({
fabric : {
type : 'Color',
uniforms : {
color : new Cesium.Color(1.0, 0.0, 0.4, 0.5)
}
}
});
}
}
catch (err) {
console.log("Error: ", err);
}
};
Promise1();
}
catch (e) {
console.log("Error:", e);
}
};
Promise0();
The polygon remains yellow, which is the default color I think.
For the image material, I use this definition locally:
new Cesium.Material({
fabric : {
type : 'Image',
uniforms : {
image : './image.png'
}
}
});

I fixed it using this way of defining the PolygonGraphics' material in my entity:
new Cesium.ImageMaterialProperty({
image: './image.png',
alpha: 0.5
});
But I noticed that alpha blending doesn't work when I try to apply it on my whole image...

Related

MV3 declarativeNetRequest and X-Frame-Options DENY

I have a MV2 extension with chrome.webRequest that works perfectly but fail on MV3 declarativeNetRequest getting around iframes.
The extension is like a multi-messenger that opens multiple iframes for various sites to merge in a single extension all popular messengers.
So I have a domain "example.com" and there I open multiple iframes, for example open an iframe with Twitter.com or Telegram.org.
Since twitter.com or telegram.org set the X-Frame-Options to DENY those iframes don't show anything.
With MV2 we could run chrome.webRequest and remove those headers:
chrome.webRequest.onHeadersReceived.addListener(
function (details)
{
if (details.tabId && (details.tabId === tabId || details.tabId === -1 || tabMultiId.includes(details.tabId))) {
var b = details.responseHeaders.filter((details) => !['x-frame-options', 'content-security-policy', 'x-content-security-policy', 'strict-transport-security', 'frame-ancestors'].includes(details.name.toLowerCase()));
b.forEach(function(e){
"set-cookie" === e.name && -1 !== e.value.indexOf("Secure") && (-1 !== e.value.indexOf("SameSite=Strict") ?
(e.value = e.value.replace(/SameSite=Strict/g, "SameSite=None"))
: -1 !== e.value.indexOf("SameSite=Lax")
? (e.value = e.value.replace(/SameSite=Lax/g, "SameSite=None"))
: (e.value = e.value.replace(/; Secure/g, "; SameSite=None; Secure")));
});
return {
responseHeaders: b
}
}
},
{
urls: [ "<all_urls>" ],
tabId: tabId
},
["blocking", "responseHeaders", "extraHeaders"]
);
I have tried to do exactly the same with MV3 but keep failing.
My 2 attemps:
async function NetRequest() {
var blockUrls = ["*://*.twitter.com/*","*://*.telegram.org/*"];
var tabId = await getObjectFromLocalStorage('tabId');
var tabMultiId = [];
tabMultiId = JSON.parse(await getObjectFromLocalStorage('tabMultiId'));
tabMultiId.push(tabId);
blockUrls.forEach((domain, index) => {
let id = index + 1;
chrome.declarativeNetRequest.updateSessionRules({
addRules:[
{
"id": id,
"priority": 1,
"action": { "type": "modifyHeaders",
"responseHeaders": [
{ "header": "X-Frame-Options", "operation": "remove" },
{ "header": "Frame-Options", "operation": "remove" },
{ "header": "content-security-policy", "operation": "remove" },
{ "header": "content-security-policy-report-only", "operation": "remove" },
{ "header": "x-content-security-policy", "operation": "remove" },
{ "header": "strict-transport-security", "operation": "remove" },
{ "header": "frame-ancestors", "operation": "remove" },
{ "header": "set-cookie", "operation": "set", "value": "SameSite=None; Secure" }
]
},
"condition": {"urlFilter": domain, "resourceTypes": ["image","media","main_frame","sub_frame","stylesheet","script","font","xmlhttprequest","ping","websocket","other"],
"tabIds" : tabMultiId }
}
],
removeRuleIds: [id]
});
});
}
async function launchWindow(newURL, windowDimensions, urlWindow, isIncognitoWindow, windowType) {
chrome.windows.create({ url: newURL, type: windowType, incognito: isIncognitoWindow, width: windowDimensions.width, height: windowDimensions.height, left: windowDimensions.left, top: windowDimensions.top },
async function (chromeWindow) {
if (urlWindow != "install" || urlWindow != "update") {
chrome.storage.local.set({ 'extensionWindowId': chromeWindow.id }, function () { });
chrome.storage.local.set({ 'tabId': chromeWindow.tabs[0].id }, function () { });
NetRequest();
}
});
}
Also tried:
const iframeHosts = [
'twitter.com', 'telegram.org'
];
const RULE = {
id: 1,
condition: {
initiatorDomains: ['example.com'],
requestDomains: iframeHosts,
resourceTypes: ['sub_frame', 'main_frame'],
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{header: 'X-Frame-Options', operation: 'remove'},
{header: 'Frame-Options', operation: 'remove'},
],
},
};
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [RULE.id],
addRules: [RULE],
});
Permissions:
"permissions": [
"system.display",
"scripting",
"activeTab",
"notifications",
"contextMenus",
"unlimitedStorage",
"storage",
"declarativeNetRequestWithHostAccess",
"webNavigation",
"alarms"
],
"host_permissions": [
"<all_urls>"
],
Any of this attempts worked.
Greetings and thank you very much for anyone that try to help.
You need to unregister service worker for the site and clear its cache using chrome.browsingData API.
Syntax for urlFilter is different, so your "*://*.twitter.com/*" is incorrect and should be "||twitter.com/", however a better solution is to use requestDomains because it allows specifying multiple sites in just one rule.
// manifest.json
"permissions": ["browsingData", "declarativeNetRequest"],
"host_permissions": ["*://*.twitter.com/", "*://*.telegram.org/"],
// extension script
async function configureNetRequest(tabId) {
const domains = [
'twitter.com',
'telegram.org',
];
const headers = [
'X-Frame-Options',
'Frame-Options',
];
await chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [1],
addRules: [{
id: 1,
action: {
type: 'modifyHeaders',
responseHeaders: headers.map(h => ({ header: h, operation: 'remove'})),
},
condition: {
requestDomains: domains,
resourceTypes: ['sub_frame'],
tabIds: [tabId],
},
}],
});
await chrome.browsingData.remove({
origins: domains.map(d => `https://${d}`),
}, {
cacheStorage: true,
serviceWorkers: true,
});
}
// Usage
chrome.windows.create({ url: 'about:blank' }, async w => {
await configureNetRequest(w.tabs[0].id);
await chrome.tabs.update(w.tabs[0].id, { url: 'https://some.real.url/' });
});

Why does react refuse to convert jsx to HTML component and just returns [object Object]?

Please find // the console log I am talking about in the below code.
This console log returns string So its not that I am passing object here. but even then react doesn't recognise the jsx and adds object Object in HTML.
Output I am getting is:
import React, { useEffect, useState } from "react";
function App() {
const [file, setFile] = useState();
useEffect(() => {
setFile(
JSON.parse(`{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}`)
);
}, []);
return (
<>
{/* <import file component> */}
<JsonViewer file={file} />
</>
);
}
function JsonViewer({ file }: any) {
const [fileContent, setFileContent] = useState<any>();
useEffect(() => {
if (file) {
setFileContent(getJsonData(file, ""));
}
}, [file]);
function getJsonData(data: any, idToAppend: string) {
let t: any = [];
if (data.length) {
console.log(data.length);
for (let i in data) {
let idToA = `${idToAppend}${i.toString()}`;
t.push(getJsonData(data[i], idToA))
}
}
else {
Object.entries(data).forEach(([key, value], i) => {
const idToA = `${idToAppend}${i.toString()}`;
if (typeof value === "object") {
let a: any = value
t.push(
<div key={i} id={idToA}>
<button
onClick={(e) => {
let ele = document.getElementById(idToA);
if (ele) {
ele.innerHTML += getJsonData(value, idToA);
}
}}
>
{key}
</button>
</div>
);
}
else {
// the console log I am talking about
console.log(typeof value);
t.push(
<div key={i}>
<div
id={idToA}
>
{key}:{value}
</div>
</div>
);
}
}
);
}
return t;
}
return (
<div>
{fileContent}
</div>
);
}
export default App;
The reason why you are getting objects is because the current procedure is creating nested t lists. You can see the output here: https://codesandbox.io/s/youthful-sunset-ru8ic?file=/src/Old.tsx
An alternative, working approach:
import React from "react";
const DATA = {
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
};
interface theDat {
data: {
short_name: string;
name: string;
icons: Icons[];
start_url: string;
display: string;
theme_color: string;
background_color: string;
};
};
type Icons = {
src: string;
sizes: string;
type: string;
};
const App = () => {
const JsonInterpreter: Function = ({data}: theDat) => {
let elements = [];
Object.keys(data).forEach((dat, i) => {
let value = data[dat];
console.log(`this: ${dat} ${typeof value}`)
if (typeof value === 'string') {
elements.push(
<p key={i}>{dat} {value}</p>
)
} else if (typeof value === 'object') {
elements.push(
<p key={i}>{dat}</p>
);
console.log(value);
let nested = [];
(value as []).forEach((d, j) => {
let keys = Object.keys(d);
if (['src', 'type', 'sizes'].every(e => keys.includes(e))) { /* typeguard for icons */
nested.push(
<p key={`n-${j}`}>{JSON.stringify(d)}</p>
);
};
})
elements.push(...nested);
};
});
return elements;
};
return (
<>
<JsonInterpreter data={DATA} />
</>
);
};
export default App;
Working CodeSandbox: https://codesandbox.io/s/youthful-sunset-ru8ic?file=/src/App.tsx:0-1789

How to access overlapping geojson/polygons to display in infobox

I'm using Google Maps API to load multiple polygons into the map using the geoJSON data layer. Some of these polygons overlap in certain regions. When a user clicks on a point that is inside of multiple polygons, I want to display the properties (name, tags, etc) in an InfoBox with the click event.
I'm wanting to display the properties of all the polygons for a given point. Currently when I click on a point I can only see one polygon, despite the point being inside of multiple polygons.
How can I access all the properties of all the polygons with Google Maps API v3?
const map = useGoogleMap(); // google map instance
const polygons; // an array of polygons, example snippet below.
map.data.addGeoJson(polygons);
map.data.addListener('click', function(event) {
// how can i access other features underneath this clicked point
console.log(event.feature); // only returns "Geofence 1"
})
example GeoJson:
polygons = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Geofence 1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-8.96484375,
-9.96885060854611
],
[
3.955078125,
-9.96885060854611
],
[
3.955078125,
-0.17578097424708533
],
[
-8.96484375,
-0.17578097424708533
],
[
-8.96484375,
-9.96885060854611
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-6.591796875,
-8.320212289522944
],
[
2.197265625,
-8.320212289522944
],
[
2.197265625,
-1.9332268264771106
],
[
-6.591796875,
-1.9332268264771106
],
[
-6.591796875,
-8.320212289522944
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 3"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-4.39453125,
-6.926426847059551
],
[
0.263671875,
-6.926426847059551
],
[
0.263671875,
-3.337953961416472
],
[
-4.39453125,
-3.337953961416472
],
[
-4.39453125,
-6.926426847059551
]
]
]
}
}
]
}
One option would be to use the containsLocation method in the geometry library.
containsLocation(point, polygon)
Parameters:
point: LatLng
polygon: Polygon
Return Value: boolean
Computes whether the given point lies inside the specified polygon.
Unfortunately that only works with native google.maps.Polygon objects not Data.Polygon objects. Translate the data in the feature into native google.maps.Polygon objects, push them on an array, then process through the array to see which polygon(s) the click is in.
create google.maps.Polygon for each polygon in the input (assumes only polygons)
var polygonArray = [];
map.data.addListener('addfeature', function(e) {
e.feature.getGeometry().getArray().forEach(function(latLngArry){
const polygon = new google.maps.Polygon({
map: map,
paths: latLngArry.getArray(),
clickable: false,
name: e.feature.getProperty("name") // save the data we want to output as an attribute
})
polygonArray.push(polygon);
})
on click check for which polygon(s) the click was in:
map.addListener('click', function(event) {
var content = "";
for (var i=0;i<polygonArray.length;i++) {
if (google.maps.geometry.poly.containsLocation(event.latLng, polygonArray[i])) {
if (content.length!=0)
content+=" : "
content += polygonArray[i].name;
}
}
console.log(content);
})
proof of concept fiddle
// This example uses the Google Maps JavaScript API's Data layer
// to create a rectangular polygon with 2 holes in it.
function initMap() {
const map = new google.maps.Map(document.getElementById("map"));
const infowindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
var polygonArray = [];
map.data.addListener('addfeature', function(e) {
console.log(e.feature.getGeometry().getArray().length);
e.feature.getGeometry().getArray().forEach(function(latLngArry) {
console.log(latLngArry.getArray())
const polygon = new google.maps.Polygon({
map: map,
paths: latLngArry.getArray(),
clickable: false,
name: e.feature.getProperty("name")
})
polygonArray.push(polygon);
})
processPoints(e.feature.getGeometry(), bounds.extend, bounds);
map.fitBounds(bounds);
});
const features = map.data.addGeoJson(polygons);
map.data.setMap(null);
map.addListener('click', function(event) {
var content = "";
for (var i = 0; i < polygonArray.length; i++) {
if (google.maps.geometry.poly.containsLocation(event.latLng, polygonArray[i])) {
if (content.length != 0)
content += " : "
content += polygonArray[i].name;
}
}
console.log(content);
document.getElementById('info').innerHTML = content;
infowindow.setPosition(event.latLng);
if (content.length == 0) content = "no GeoFence";
infowindow.setContent(content);
infowindow.open(map);
})
function processPoints(geometry, callback, thisArg) {
if (geometry instanceof google.maps.LatLng) {
callback.call(thisArg, geometry);
} else if (geometry instanceof google.maps.Data.Point) {
callback.call(thisArg, geometry.get());
} else {
geometry.getArray().forEach(function(g) {
processPoints(g, callback, thisArg);
});
}
}
}
const polygons = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"name": "Geofence 1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-8.96484375, -9.96885060854611],
[
3.955078125, -9.96885060854611
],
[
3.955078125, -0.17578097424708533
],
[-8.96484375, -0.17578097424708533],
[-8.96484375, -9.96885060854611]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-6.591796875, -8.320212289522944],
[
2.197265625, -8.320212289522944
],
[
2.197265625, -1.9332268264771106
],
[-6.591796875, -1.9332268264771106],
[-6.591796875, -8.320212289522944]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Geofence 3"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-4.39453125, -6.926426847059551],
[
0.263671875, -6.926426847059551
],
[
0.263671875, -3.337953961416472
],
[-4.39453125, -3.337953961416472],
[-4.39453125, -6.926426847059551]
]
]
}
}
]
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 90%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<title>Data Layer: Polygon</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly" defer></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="info"></div>
<div id="map"></div>
</body>
</html>
I was able to get this POC working with React and am sharing for next person who might be interested:
import { InfoWindow, useGoogleMap } from '#react-google-maps/api';
export const GeofencesContainer = () => {
const map = useGoogleMap();
// geofence = geoJSON feature collections
const [geofenceClickEventLatLng, setGeofenceClickEventLatLng] = useState({});
const [geofencesProperties, setGeofencesProperties] = useState([]);
const [isInfoWindowOpen, setIsInfoWindowOpen] = useState(false);
const [polygonArray, setPolygonArray] = useState([]);
const ref = useRef();
useEffect(() => {
let addFeatureListener;
const getGeofences = async () => {
if (true) {
if (ref.current !== 'geofenceEnabled') {
// get geofences from api
const geofences = await fetch.getGeofences();
// a listener to add feature, where we create a google.maps.polygon for every added feature, then add it to the polygon array
const addedPolygons = [];
addFeatureListener = map.data.addListener('addfeature', e => {
const featureGeometry = e.feature.getGeometry().getArray();
// for each geometry get the latLng array, which will be used to form a polygon
featureGeometry.forEach(latLngArray => {
const polygon = new window.google.maps.Polygon({
map,
paths: latLngArray.getArray(),
strokeWeight: 1,
fillColor: 'green',
clickable: false,
name: e.feature.getProperty('name')
});
addedPolygons.push(polygon);
});
setPolygonArray(addedPolygons);
});
// add the polygon to the map data layer (this will show the polygon on the map and cause the addfeature listener to fire)
map.data.addGeoJson(geofences);
map.data.setStyle({ fillColor: 'green', strokeWeight: 1 });
// we set map data to null so that we don't end up with 2 polygons on top of each other
map.data.setMap(null);
ref.current = 'geofenceEnabled';
}
} else {
ref.current = null;
for (const polygon of polygonArray) {
polygon.setMap(null);
}
setIsInfoWindowOpen(false);
}
};
getGeofences();
return () => {
if (addFeatureListener) {
addFeatureListener.remove();
}
};
}, [activeKmls]);
useEffect(() => {
let clickListener;
if (true) {
// a listener on click that checks whether the point is in a polygon and updates the necessary state to show the proper info window
clickListener = map.addListener('click', function(event) {
// this state is updated to identify the place where the info window will open
setGeofenceClickEventLatLng(event.latLng);
// for every polygon in the created polygons array check if it includes the clicked point, then update what to display in the info window
const selectedGeofences = [];
for (const polygon of polygonArray) {
if (window.google.maps.geometry.poly.containsLocation(event.latLng, polygon)) {
const name = polygon.name;
selectedGeofences.push({ key: name, name });
}
}
if (selectedGeofences.length) {
setIsInfoWindowOpen(true);
} else {
setIsInfoWindowOpen(false);
}
setGeofencesProperties(selectedGeofences);
});
}
return () => {
if (clickListener) {
clickListener.remove();
}
};
}, [activeKmls, polygonArray, altitudeUnit, map]);
return (
<Fragment>
{isInfoWindowOpen && (
<InfoWindow
position={geofenceClickEventLatLng}
onCloseClick={() => setIsInfoWindowOpen(false)}
zIndex={0}
options={{ maxWidth: '450' }}
>
<Fragment>
{geofencesProperties.map(geofenceProperties => {
return (
<Fragment key={geofenceProperties.key}>
<Title level={4}>{geofenceProperties.name}</Title>
</Fragment>
);
})}
</Fragment>
</InfoWindow>
)}
</Fragment>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Can't get a sprite animation running in Pixijs on React

I am trying to follow along with the Pixijs guide provided here:
https://pixijs.github.io/examples/#/demos/animatedsprite-demo.js
- and after a bit of digging here is the sheet they use for their texture mapper
https://github.com/pixijs/examples/blob/gh-pages/required/assets/mc.json
To get an example up of a simple animated sprite. The issue that I am having is that I am following along almost exactly and I am getting an error - I do not know what is causing the problem and I don't know how to proceed debugging on my own.
The example has:
var app = new PIXI.Application();
document.body.appendChild(app.view);
app.stop();
PIXI.loader
.add('spritesheet', 'required/assets/mc.json')
.load(onAssetsLoaded);
function onAssetsLoaded() {
// create an array to store the textures
var explosionTextures = [],
i;
for (i = 0; i < 26; i++) {
var texture = PIXI.Texture.fromFrame('Explosion_Sequence_A ' + (i+1) + '.png');
explosionTextures.push(texture);
}
Where I have:
componentDidMount(){
this.renderer = PIXI.autoDetectRenderer(1366, 768);
this.refs.gameCanvas.appendChild(this.renderer.view);
this.stage = new PIXI.Container();
this.stage.width = 400;
this.stage.height = 400;
console.log(littlemarioforwardwalkjson)
PIXI.loader
.add(littlemarioforwardwalkpng, littlemarioforwardwalkjson)
.load(()=>this.spriteLoaded());
// console.log(PIXI.utils.TextureCache);
}
spriteLoaded(){
console.log('yolo');
var frames = [];
var index = 0;
console.log('hello there sailor');
console.log(PIXI.utils.TextureCache)
for (var i = 0; i < 3; i++) {
index = i+46;
var texture = PIXI.Texture.fromFrame("mario_characters1_"+index+".png");
marioTextures.push(texture);
}
}
The error I am getting is:
Error: the frameId “mario_characters1_46.png” does not exist in the texture cache
This is frustrating as my texturepacker json file is displaying correctly:
{"frames": {
"mario_characters1_46.png":
{
"frame": {"x":0,"y":0,"w":12,"h":15},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":12,"h":15},
"sourceSize": {"w":12,"h":15},
"pivot": {"x":0.5,"y":0.5}
},
"mario_characters1_47.png":
{
"frame": {"x":12,"y":0,"w":11,"h":16},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":11,"h":16},
"sourceSize": {"w":11,"h":16},
"pivot": {"x":0.5,"y":0.5}
},
"mario_characters1_48.png":
{
"frame": {"x":23,"y":0,"w":15,"h":16},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":15,"h":16},
"sourceSize": {"w":15,"h":16},
"pivot": {"x":0.5,"y":0.5}
}},
"meta": {
"app": "http://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "littlemarioforwardwalk.png",
"format": "RGBA8888",
"size": {"w":38,"h":16},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:ae9c1a55b9f5884f4a4c0182ea720ca9:80c341baf7877296bb8143f4c51a5998:383ea93646790c53db2201f0624e779e$"
}
}
If I console.log(PIXI.utils.TextureCache) I get:
{data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAQCAYAAAB6Hg0eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABUUlEQVRIx6VVPZqFIAxM/DwE1+B1WL5j0NrucWxtPcYr106uwS2yxQobMWB0aVSciTP5QYRikTVU7mGI+BT3lIdXJCmIFqcRVeP1fHN9xXzvrP/dCws46wG/FryLK9cdXpduvkegBE7Xg9vJE02etLhy/y6vE52F5eCMP2txkrg7POSOJDc1UVe4YT72rzZ+4qGU0mpjsj7Q4p7GF13lrGwG1leEYQakyVMiZvf7+1qWWnH5MEi8QwY0ZSsxrY+k7NQ4tUnNzZ8CcIKmRBk/vsFtBtxmxFI5689ZGd85RjbHDJxKc3Xw1coiYfOE7YZaByyPP8yAfesDYllZXznrAez+Yv60h+Xi1CdriJe1KzMg/U74M4aIJxNM1NNfUbn6v4aNhylaoTExISKBIdg+pwaGcJ7IFeKhIlx8TbRqvCVxUqYlPMfVjhO1MM3SiP+PuB9QuQ5f9MhyUAAAAABJRU5ErkJggg==: Texture}
So it would seem that the error is saying that the Texture Cache is only seeing one image blob - however, calling Texture.fromFrame is how the example on the website says to get it to work, and I think I am reproducing the code very closely.
If anyone has any ideas please let me know.
It was not easy to find an example with React, if that can help someone.
import React from "react";
import KingHuman from "./img/kinghuman/Idle/Idle.png";
import KingHumanJson from "./img/kinghuman/Idle/Idle.json";
import * as PIXI from 'pixi.js';
import { Stage, Container, AnimatedSprite } from '#inlet/react-pixi';
const PixiGame = () => {
const willMount = useRef(true);
const [textures, setTextures] = useState([]);
const loadSpritesheet = () => {
const baseTexture = PIXI.BaseTexture.from(KingHuman);
const spritesheet = new PIXI.Spritesheet(baseTexture, KingHumanJson);
spritesheet.parse(() => {
setTextures( Object.keys(spritesheet.textures).map((t) => spritesheet.textures[t]));
});
}
// Hooks way to do ComponentWillMount
if (willMount.current) {
loadSpritesheet();
willMount.current = false;
}
return (
<Stage width={300} height={300} options={{ backgroundColor: 0xeef1f5 }}>
<Container position={[150, 150]}>
<AnimatedSprite
anchor={0.5}
textures={textures}
isPlaying={true}
initialFrame={0}
animationSpeed={0.1}
/>
</Container>
</Stage>
);
}
export default PixiGame;
Yes I also struggled to get information for react on this. Had a sprite sheet without a json and created a function to generate the frame data. Adding on to the other answer is my code snippet
import React, { useRef, useState } from "react";
import KingHuman from "../../assets/mc.png";
import * as PIXI from 'pixi.js';
import { Stage, Container, AnimatedSprite } from '#inlet/react-pixi';
const generateFrames = (animationWidth, animationHeight, rowSize, colSize, fileWidth, fileHeight, imageName) => {
let generated = {
"frames": {},
"meta": {
"app": "Splash Software Assessment",
"version": "1.0",
"image": imageName,
"format": "RGBA8888",
"size": { "w": fileWidth, "h": fileHeight },
"scale": "1",
"smartupdate": ""
}
};
for (let i = 0; i < rowSize; i++) {
for (let j = 0; j < colSize; j++) {
const px = animationWidth * i;
const py = animationHeight * j;
const image = `${imageName}${px}${py}.png`
generated.frames[image] = {
"frame": { "x": px, "y": py, "w": animationWidth, "h": animationHeight },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": px, "y": py, "w": animationWidth, "h": animationHeight },
"sourceSize": { "w": animationWidth, "h": animationHeight }
}
}
}
return generated;
};
const PixiGame = () => {
const willMount = useRef(true);
const [textures, setTextures] = useState([]);
const KingHumanJson = generateFrames(240, 240, 4, 8, 1024, 2048, "mc.png")
const loadSpritesheet = async () => {
const baseTexture = PIXI.BaseTexture.from(KingHuman);
const spritesheet = new PIXI.Spritesheet(baseTexture, KingHumanJson);
const textures = await spritesheet.parse();
setTextures(Object.keys(textures).map((t) => textures[t]));
}
// Hooks way to do ComponentWillMount
if (willMount.current) {
loadSpritesheet();
willMount.current = false;
}
return (
<Stage width={300} height={300} options={{ backgroundColor: 0xeef1f5 }}>
<Container position={[150, 150]}>
<AnimatedSprite
anchor={0.5}
textures={textures}
isPlaying={true}
initialFrame={0}
animationSpeed={0.1}
/>
</Container>
</Stage>
);
}
export default PixiGame;

How to display nested JSON in sap.m Table (SAPUI5) for each listItem

I have a sap.m splitApp where i have an overview of courses. By displaying a course you get detail information like the list of participants for that course. Currently it is only possible to display the participants of the same (one) course for all courses. How can i display the appropriate participants for each course.
If anyone has an idea that would be great :) Thanks.
This is my "Details.view"
sap.ui.jsview("tem_trainer.Details", {
/** Specifies the Controller belonging to this View.
* In the case that it is not implemented, or that "null" is returned, this View does not have a Controller.
* #memberOf tem_trainer.Details
*/
getControllerName : function() {
return "tem_trainer.Details";
},
onBeforeFirstShow: function(oEvent){
this.getController().onBeforeFirstShow(oEvent);
},
/** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed.
* Since the Controller is given to this method, its event handlers can be attached right away.
* #memberOf tem_trainer.Details
*/
createContent : function(oController) {
function dateDiffInDays(a, b) {
// Discard the time and time-zone information.
var utc1 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
var utc2 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
return Math.floor((utc2 - utc1) / (1000 * 60 * 60 * 24));
}
var oTimestamp = new sap.m.ObjectAttribute({
text: '{start} - {end}, {starttime} - {endtime}',
});
var oRoom = new sap.m.ObjectAttribute({
text: "{room}",
});
var oHeader = new sap.m.ObjectHeader({
title: "{name}",
number: "{start}",
numberUnit: "Start Date",
attributes: [
oTimestamp, oRoom
]
});
var oTable = new sap.m.Table("idRandomDataTable", {
headerToolbar : new sap.m.Toolbar({
content : [ new sap.m.Label({
text : "Participant List"
}), new sap.m.ToolbarSpacer({}), new sap.m.Button("idPersonalizationButton", {
icon : "sap-icon://person-placeholder"
}) ]
}),
columns : [
new sap.m.Column({
width : "2em",
header : new sap.m.Label({
text : "Firstname"
})
}),
new sap.m.Column({
width : "2em",
header : new sap.m.Label({
text : "Lastname"
})
}),
new sap.m.Column({
width : "2em",
header : new sap.m.Label({
text : "Job"
})
}),
new sap.m.Column({
width : "2em",
header : new sap.m.Label({
text : "Company"
})
})
]
});
var oModel1 = new sap.ui.model.json.JSONModel();
var model = sap.ui.getCore().getModel();
var aData = model.getProperty("/courses");
oModel1.setData({
modelData : aData
});
oTable.setModel(oModel1);
oTable.bindItems("/modelData/0/participant", new sap.m.ColumnListItem({
cells : [ new sap.m.Text({
text : "{firstname}"
}), new sap.m.Text({
text : "{lastname}"
}), new sap.m.Text({
text : "{job}",
}), new sap.m.Text({
text : "{company}",
}),]
}));
var oIconTabBar = new sap.m.IconTabBar({
items: [
new sap.m.IconTabFilter({
text: "General",
icon: "sap-icon://hint",
content: [
]
}),
new sap.m.IconTabFilter({
text: "Participants",
icon: "sap-icon://visits",
content: [
oTable
]
}),
]
});
return this.page = new sap.m.Page({
title: "Course Details",
content: [
oHeader, oIconTabBar
]
});
}
});
This is my "Details.controller"
sap.ui.controller("tem_trainer.Details", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* #memberOf tem_trainer.Details
*/
// onInit: function() {
//
// },
/**
* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
* (NOT before the first rendering! onInit() is used for that one!).
* #memberOf tem_trainer.Details
*/
// onBeforeRendering: function() {
//
// },
/**
* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* #memberOf tem_trainer.Details
*/
// onAfterRendering: function() {
//
// },
/**
* Called when the Controller is destroyed. Use this one to free resources and finalize activities.
* #memberOf tem_trainer.Details
*/
// onExit: function() {
//
// }
onBeforeFirstShow: function(oEvent){
if(oEvent.data.bindingContext){
// Binding Kontext setzen
this.getView().page.setBindingContext(oEvent.data.bindingContext);
}
},
onListSelect: function(oEvent){
var oBindingContext = oEvent.getParameter(
"listItem").getBindingContext();
var sViewId = "detailCourse_" +
oEvent.getParameter(
"listItem").data("req_id");
sap.ui.getCore().getEventBus().publish(
"nav",
"to", {
viewName: "tem_trainer.Details",
viewId: sViewId,
data: {
bindingContext: oBindingContext
}
});
},
onListItemTap: function(oEvent){
var oBindingContext = oEvent.oSource.getBindingContext();
var sViewId = "detailCourse_" +
oEvent.oSource.data("req_id");
sap.ui.getCore().getEventBus().publish(
"nav",
"to", {
viewName: "tem_trainer.Details",
viewId: sViewId,
data: {
bindingContext: oBindingContext
}
});
},
onNavButtonTap: function(){
// Wird ausgeführt wenn die Hardwaretaste
// oder der Back-Button gedrückt wird
sap.ui.getCore().getEventBus().publish(
"nav", "back");
}
});
This is my "Courses.json"
{
"courses": [
{
"req_id": "1",
"name" : "ABAP OO Basics",
"start" : "20-08-2014",
"end" : "22-08-2014",
"starttime": "10:00:00",
"endtime": "16:00:00",
"status": "Booked",
"room": "Room CDE",
"adress" : "Adress No.1",
"street": "Street No.1",
"zip_code": "74892142578485",
"city": "City No.1",
"country": "Country No.1",
"phone": "0595726764675435497436497",
"fax":"12",
"cap_min": "10",
"cap_opt": "20",
"cap_max": "30",
"img": "./res/1.jpg",
"content": "Test",
"participant": [{ "firstname": "Maik",
"lastname": "Maier",
"job": "installer",
"company": "muster"
},
{ "firstname": "Marco",
"lastname": "Schmidt",
"job": "installer",
"company": "schmitt"
},
{ "firstname": "Hans",
"lastname": "Mueller",
"job": "installer",
"company": "muster"
},
{ "firstname": "Matthias",
"lastname": "Gottlieb",
"job": "installer",
"company": "schmitt"
}]
},
{
"req_id": "2",
"name" : "ABAP OO Basics II",
"start" : "22-08-2014",
"end" : "23-08-2014",
"starttime": "11:00:00",
"endtime": "14:00:00",
"status": "Booked",
"room": "Room XYZ",
"adress" : "Adress No.2",
"street": "Street No.2",
"zip_code": "2222",
"city": "City No.2",
"country": "Country No.2",
"phone": "22222",
"fax":"2222",
"cap_min": "10",
"cap_opt": "20",
"cap_max": "30",
"img": "./res/2.jpg",
"content": "Test",
"participant": [{ "firstname": "Name",
"lastname": "Name",
"job": "installer",
"company": "muster"
},
{ "firstname": "Name2",
"lastname": "Name2",
"job": "installer",
"company": "schmitt"
}]
}
]
}
EDIT: Here is my "Master.controller"
sap.ui.controller("tem_trainer.Master", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* #memberOf tem_trainer.Master
*/
// onInit: function() {
//
// },
/**
* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
* (NOT before the first rendering! onInit() is used for that one!).
* #memberOf tem_trainer.Master
*/
onBeforeRendering: function() {
var oInbox = sap.ui.getCore().byId("inboxList");
oInbox.removeSelections();
var oJSONDataModel = new sap.ui.model.json.JSONModel();
oJSONDataModel.loadData("./json/Courses.json");
sap.ui.getCore().setModel(oJSONDataModel);
},
/**
* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* #memberOf tem_trainer.Master
*/
// onAfterRendering: function() {
//
// },
/**
* Called when the Controller is destroyed. Use this one to free resources and finalize activities.
* #memberOf tem_trainer.Master
*/
// onExit: function() {
//
// }
onBeforeFirstShow: function(oEvent) {
this.bindListData();
if(oEvent.data.title) {
this.getView().page.setTitle(oEvent.data.title);
}
},
bindListData: function(aFilters){
var that = this;
this.getView().oList.bindAggregation("items", {
path: "/courses",
factory: function(sId){
return new sap.m.StandardListItem(sId, {
icon : "sap-icon://course-program",
title: {
path:"name",
},
description: {
path:"start",
},
type: jQuery.device.is.phone?
sap.m.ListType.Navigation : sap.m.ListType.None,
customData: [
new sap.ui.core.CustomData({
key: "req_id",
value: "{req_id}"
}),
],
tap: [that.onListItemTap, that]
}).setInfoState(sap.ui.core.ValueState.Success);
},
filters: aFilters
});
},
onListSelect: function(oEvent){
var oBindingContext =
oEvent.getParameter(
"listItem"
).getBindingContext(),
sViewId = "detailCourse_" +
oEvent.getParameter(
"listItem").data("req_id");
// Ereignis an EventBus übergeben
sap.ui.getCore().getEventBus().publish(
"nav",
"to", {
viewName: "tem_trainer.Details",
viewId: sViewId,
data: {
bindingContext: oBindingContext
}
});
},
onListItemTap: function(oEvent){
// siehe onListSelect
var oBindingContext =
oEvent.oSource.getBindingContext(),
sViewId = "detailCourse_" +
oEvent.oSource.data("req_id");
sap.ui.getCore().getEventBus().publish(
"nav",
"to", {
viewName: "tem_trainer.Details",
viewId: sViewId,
data: {
bindingContext: oBindingContext
}
});
},
//Navigation Zurück
onNavButtonTap: function(){
sap.ui.getCore().getEventBus().publish(
"nav",
"back"
);
},
//Search Functionality
handleSearch: function(oEvent) {
this._updateList();
},
_updateList : function () {
var filters = [];
//var oView = this.getView();
// Filter für die Suche
//var searchString = oView.byId("searchField").getValue();
var searchString = sap.ui.getCore().byId("searchField").getValue();
if (searchString && searchString.length > 0) {
var filter = new sap.ui.model.Filter("name", sap.ui.model.FilterOperator.Contains, searchString);
filters.push(filter);
}
// List Binding updaten
//var list = oView.byId("inboxList");
var list = sap.ui.getCore().byId("inboxList");
var binding = list.getBinding("items");
binding.filter(filters);
},
});
EDIT: This is what i adjusted:
Details.controller
onInit : function() {
var bus = sap.ui.getCore().getEventBus();
bus.subscribe("nav", "to", this.navToHandler, this);
},
navToHandler : function(channelId, eventId, data) {
if (data && data.viewId) {
var oBindingContext = data.data.bindingContext;
this.getView().setBindingContext(oBindingContext);
}
},
//This is a function i already had and which is called by the view
onBeforeFirstShow: function(oEvent){
if(oEvent.data.bindingContext){
// Binding Kontext setzen
this.getView().page.setBindingContext(oEvent.data.bindingContext);
}
},
In my Details.view
var oModel1 = new sap.ui.model.json.JSONModel();
var model = sap.ui.getCore().getModel();
var aData = model.getProperty("/courses");
oModel1.setData({
modelData : aData
});
oTable.setModel(oModel1);
oTable.bindItems("participant", new sap.m.ColumnListItem({
cells : [ new sap.m.Text({
text : "{firstname}"
}), new sap.m.Text({
text : "{lastname}"
}), new sap.m.Text({
text : "{job}",
}), new sap.m.Text({
text : "{company}",
}),]
}));
Your requirement is similar to the demo app of Approve Purchase Orders.
See the json string:
https://openui5.hana.ondemand.com/test/resources/sap/m/demokit/poa/model/mock.json
Courses 1 : n Course 1 : n Participants
PurchaseOrderCollection 1 : n PurchaseOrder 1 :n PurchaseOrder_Items
You already did publish eventbus in your master controller when use selects the list item in the master view:
onListSelect: function(oEvent){
var oBindingContext =
oEvent.getParameter(
"listItem"
).getBindingContext(),
sViewId = "detailCourse_" +
oEvent.getParameter(
"listItem").data("req_id");
// Ereignis an EventBus übergeben
sap.ui.getCore().getEventBus().publish(
"nav",
"to", {
viewName: "tem_trainer.Details",
viewId: sViewId,
data: {
bindingContext: oBindingContext
}
});
},
You need to subscribe the eventbus in your Detail view controller to get the Binding Context of your list item and set the binding context for your detail view, in your case the single course.
onInit : function() {
var bus = sap.ui.getCore().getEventBus();
bus.subscribe("nav", "to", this.navToHandler, this);
},
navToHandler : function(channelId, eventId, data) {
if (data && data.viewId) {
var oBindingContext = data.data.bindingContext;
this.getView().setBindingContext(oBindingContext);
}
},
And adjust your binding paths of the table in the detail view, do not hard code 0.
oTable.bindItems("participant", new sap.m.ColumnListItem({
cells : [ new sap.m.Text({
text : "{firstname}"
}), new sap.m.Text({
text : "{lastname}"
}), new sap.m.Text({
text : "{job}",
}), new sap.m.Text({
text : "{company}",
}),]
}));