diff --git a/chart2/qa/extras/chart2import2.cxx b/chart2/qa/extras/chart2import2.cxx index 65434029e21d..04e0770dd143 100644 --- a/chart2/qa/extras/chart2import2.cxx +++ b/chart2/qa/extras/chart2import2.cxx @@ -1175,6 +1175,68 @@ CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramODSRoundtrip) CPPUNIT_ASSERT_EQUAL(3.5, fBinWidth); } +CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testCorrelationCircleODSRoundtrip) +{ + loadFromFile(u"fods/correlation-circle.fods"); + + saveAndReload(TestFilter::ODS); + + xmlDocUniquePtr pXmlDoc = parseExport(u"Object 1/content.xml"_ustr); + CPPUNIT_ASSERT(pXmlDoc); + + // A reader that knows nothing of this chart type is left with points in a + // plane, and the name of the type itself rides in the loext namespace. + assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart" + "[@chart:class='chart:scatter']"); + assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart" + "[@loext:class='loext:correlation-circle']"); + + // Each series keeps its feature column as the series range and the two + // dimension columns as a domain each. + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:chart/chart:chart" + "/chart:plot-area/chart:series", + 3); + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:chart/chart:chart" + "/chart:plot-area/chart:series[1]/chart:domain", + 2); + + uno::Reference xChartDoc = getChartDocFromSheet(0); + CPPUNIT_ASSERT(xChartDoc.is()); + + Reference xChartType = getChartTypeFromDoc(xChartDoc, 0, 0); + CPPUNIT_ASSERT(xChartType.is()); + CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.CorrelationCircleChartType"_ustr, + xChartType->getChartType()); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getNumberOfDataSeries(xChartDoc)); + + // One series per feature column, every one of them against the same two + // dimension columns. + const OUString aFeatureRanges[] + = { u"$Sheet1.$A$2:$A$6"_ustr, u"$Sheet1.$B$2:$B$6"_ustr, u"$Sheet1.$C$2:$C$6"_ustr }; + for (sal_Int32 nSeries = 0; nSeries < 3; ++nSeries) + { + Reference xFeature + = getDataSequenceFromDocByRole(xChartDoc, u"values-feature", nSeries); + CPPUNIT_ASSERT(xFeature.is()); + CPPUNIT_ASSERT_EQUAL(aFeatureRanges[nSeries], xFeature->getSourceRangeRepresentation()); + + Reference xFirstDimension + = getDataSequenceFromDocByRole(xChartDoc, u"values-x", nSeries); + CPPUNIT_ASSERT(xFirstDimension.is()); + CPPUNIT_ASSERT_EQUAL(u"$Sheet1.$D$2:$D$6"_ustr, + xFirstDimension->getSourceRangeRepresentation()); + + Reference xSecondDimension + = getDataSequenceFromDocByRole(xChartDoc, u"values-y", nSeries); + CPPUNIT_ASSERT(xSecondDimension.is()); + CPPUNIT_ASSERT_EQUAL(u"$Sheet1.$E$2:$E$6"_ustr, + xSecondDimension->getSourceRangeRepresentation()); + } +} + CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramXLSXRoundtrip) { // See tdf#163727 diff --git a/chart2/qa/extras/data/fods/correlation-circle.fods b/chart2/qa/extras/data/fods/correlation-circle.fods new file mode 100644 index 000000000000..3dea685046f8 --- /dev/null +++ b/chart2/qa/extras/data/fods/correlation-circle.fods @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Column D + + + Column E + + + length + + Sheet1.A1:Sheet1.A1 + + + width + + Sheet1.B1:Sheet1.B1 + + + mass + + Sheet1.C1:Sheet1.C1 + + + + + + + 1 + + + -0.6 + + Sheet1.D2:Sheet1.D6 + + + 0.1 + + Sheet1.E2:Sheet1.E6 + + + 1 + + Sheet1.A2:Sheet1.A6 + + + 5 + + Sheet1.B2:Sheet1.B6 + + + 40 + + Sheet1.C2:Sheet1.C6 + + + + + 2 + + + -0.3 + + + -0.4 + + + 2 + + + 4 + + + 38 + + + + + 3 + + + 0 + + + 0.5 + + + 3 + + + 3.5 + + + 39 + + + + + 4 + + + 0.3 + + + -0.3 + + + 4 + + + 2 + + + 36 + + + + + 5 + + + 0.6 + + + 0.1 + + + 5 + + + 1 + + + 35 + + + + + + + + + + + + + + length + + + width + + + mass + + + dim 1 + + + dim 2 + + + + + 1 + + + 5 + + + 40 + + + -0.6 + + + 0.1 + + + + + 2 + + + 4 + + + 38 + + + -0.3 + + + -0.4 + + + + + 3 + + + 3.5 + + + 39 + + + 0 + + + 0.5 + + + + + 4 + + + 2 + + + 36 + + + 0.3 + + + -0.3 + + + + + 5 + + + 1 + + + 35 + + + 0.6 + + + 0.1 + + + + + + + diff --git a/chart2/source/controller/chartapiwrapper/ChartDocumentWrapper.cxx b/chart2/source/controller/chartapiwrapper/ChartDocumentWrapper.cxx index fc7aeb035891..c7752a1b5839 100644 --- a/chart2/source/controller/chartapiwrapper/ChartDocumentWrapper.cxx +++ b/chart2/source/controller/chartapiwrapper/ChartDocumentWrapper.cxx @@ -87,6 +87,7 @@ enum eServiceType SERVICE_NAME_STOCK_DIAGRAM, SERVICE_NAME_XY_DIAGRAM, SERVICE_NAME_BUBBLE_DIAGRAM, + SERVICE_NAME_CORRELATION_CIRCLE_DIAGRAM, SERVICE_NAME_DASH_TABLE, SERVICE_NAME_GRADIENT_TABLE, @@ -116,6 +117,7 @@ tServiceNameMap & lcl_getStaticServiceNameMap() {"com.sun.star.chart.StockDiagram", SERVICE_NAME_STOCK_DIAGRAM}, {"com.sun.star.chart.XYDiagram", SERVICE_NAME_XY_DIAGRAM}, {"com.sun.star.chart.BubbleDiagram", SERVICE_NAME_BUBBLE_DIAGRAM}, + {"com.sun.star.chart.CorrelationCircleDiagram", SERVICE_NAME_CORRELATION_CIRCLE_DIAGRAM}, {"com.sun.star.drawing.DashTable", SERVICE_NAME_DASH_TABLE}, {"com.sun.star.drawing.GradientTable", SERVICE_NAME_GRADIENT_TABLE}, @@ -1230,6 +1232,14 @@ uno::Reference< uno::XInterface > SAL_CALL ChartDocumentWrapper::createInstance( bCreateDiagram = true; } break; + case SERVICE_NAME_CORRELATION_CIRCLE_DIAGRAM: + if( xChartTypeManager.is()) + { + xTemplate = xChartTypeManager->createTemplate( + u"com.sun.star.chart2.template.CorrelationCircle"_ustr); + bCreateDiagram = true; + } + break; case SERVICE_NAME_DASH_TABLE: case SERVICE_NAME_GRADIENT_TABLE: diff --git a/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx b/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx index e85193eab722..8f7970f3b7cf 100644 --- a/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx +++ b/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx @@ -570,6 +570,9 @@ OUString lcl_getDiagramType( std::u16string_view rTemplateServiceName ) if( aName.find( u"Bubble" ) != std::u16string_view::npos ) return u"com.sun.star.chart.BubbleDiagram"_ustr; + if( aName.find( u"CorrelationCircle" ) != std::u16string_view::npos ) + return u"com.sun.star.chart.CorrelationCircleDiagram"_ustr; + // Note: this must be checked after Bar, Net and Scatter // "Symbol" "StackedSymbol" "PercentStackedSymbol" "Line" "StackedLine" @@ -607,6 +610,7 @@ OUString lcl_getOldChartTypeName( const OUString & rNewChartTypeName ) {u"com.sun.star.chart2.NetChartType", u"com.sun.star.chart.NetDiagram"_ustr}, {u"com.sun.star.chart2.CandleStickChartType", u"com.sun.star.chart.StockDiagram"_ustr}, {u"com.sun.star.chart2.BubbleChartType", u"com.sun.star.chart.BubbleDiagram"_ustr}, + {u"com.sun.star.chart2.CorrelationCircleChartType", u"com.sun.star.chart.CorrelationCircleDiagram"_ustr}, {u"com.sun.star.chart2.FunnelChartType", u"com.sun.star.chart.FunnelDiagram"_ustr}, {u"com.sun.star.chart2.BoxWhiskerChartType", u"com.sun.star.chart.BoxWhiskerDiagram"_ustr}, {u"com.sun.star.chart2.ClusteredColumnChartType", u"com.sun.star.chart.ClusteredColumnDiagram"_ustr}, diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 9241b929e46e..abf0ec8e73ff 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -537,6 +537,7 @@ namespace xmloff::token { XML_COPY_STYLES, XML_CORNER_RADIUS, XML_CORRECT, + XML_CORRELATION_CIRCLE, XML_COS, XML_COSH, XML_COT, diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 86bf48416009..ab9cdc791ce8 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -573,6 +573,7 @@ $(eval $(call gb_UnoApi_add_idlfiles_noheader,offapi,com/sun/star/chart,\ ChartTitle \ ChartTwoAxisXSupplier \ ChartTwoAxisYSupplier \ + CorrelationCircleDiagram \ Diagram \ Dim3DDiagram \ DonutDiagram \ diff --git a/offapi/com/sun/star/chart/CorrelationCircleDiagram.idl b/offapi/com/sun/star/chart/CorrelationCircleDiagram.idl new file mode 100644 index 000000000000..875268c26547 --- /dev/null +++ b/offapi/com/sun/star/chart/CorrelationCircleDiagram.idl @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * Copyright the LibreOffice contributors. + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +module com { module sun { module star { module chart { + +/** a service for correlation circle plots. + +

Each series holds one feature column together with the two dimension + columns every series shares, and stands for one point, at the correlation of + the feature with each of the two dimensions.

+ */ +service CorrelationCircleDiagram +{ + service com::sun::star::chart::Diagram; + service com::sun::star::chart::ChartAxisXSupplier; + service com::sun::star::chart::ChartTwoAxisYSupplier; +}; + +}; }; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index 54b789e15031..710efd324ce9 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -2917,6 +2917,7 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. loext:histogram + loext:correlation-circle diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx index 76c05a6bcb4f..715ea5d5cd9f 100644 --- a/xmloff/source/chart/SchXMLExport.cxx +++ b/xmloff/source/chart/SchXMLExport.cxx @@ -1321,6 +1321,18 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< chart::XChartDocument > mrExport.GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_LO_EXT, GetXMLToken(XML_HISTOGRAM))); } + else if (eXMLChartType == XML_CORRELATION_CIRCLE) + { + // A reader that does not know the correlation circle plot is + // left with points in a plane, which is the nearest thing the + // format has a name for. + mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_CLASS, + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_CHART, GetXMLToken(XML_SCATTER))); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CLASS, + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_LO_EXT, GetXMLToken(XML_CORRELATION_CIRCLE))); + } else if( eXMLChartType != XML_TOKEN_INVALID ) { mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, @@ -3007,8 +3019,13 @@ void SchXMLExportHelper_Impl::exportSeries( { bool bIsScatterChart = aChartType == "com.sun.star.chart2.ScatterChartType"; bool bIsBubbleChart = aChartType == "com.sun.star.chart2.BubbleChartType"; + // A correlation circle plot keeps its two dimension + // columns where a bubble chart keeps its x and y + // values, in a domain each. + bool bHasTwoDomains = bIsBubbleChart + || aChartType == "com.sun.star.chart2.CorrelationCircleChartType"; Reference< chart2::data::XDataSequence > xYValuesForBubbleChart; - if( bIsBubbleChart ) + if( bHasTwoDomains ) { Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-y"_ustr ) ); if( xSequence.is() ) @@ -3018,7 +3035,7 @@ void SchXMLExportHelper_Impl::exportSeries( xYValuesForBubbleChart = nullptr; } } - if( bIsScatterChart || bIsBubbleChart ) + if( bIsScatterChart || bHasTwoDomains ) { Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-x"_ustr ) ); if( xSequence.is() ) diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx index aaa3e76c3584..1e015fb5f859 100644 --- a/xmloff/source/chart/SchXMLSeries2Context.cxx +++ b/xmloff/source/chart/SchXMLSeries2Context.cxx @@ -469,6 +469,8 @@ void SchXMLSeries2Context::startFastElement (sal_Int32 /*Element*/, OUString aMainRole(u"values-y"_ustr); if (maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType") aMainRole = "values-size"; + else if (maSeriesChartTypeName == "com.sun.star.chart2.CorrelationCircleChartType") + aMainRole = "values-feature"; xSeqProp->setPropertyValue(u"Role"_ustr, uno::Any(aMainRole)); } xLabeledSeq->setValues(xSequenceValues); @@ -566,12 +568,15 @@ void SchXMLSeries2Context::endFastElement(sal_Int32 ) // roles are not yet saved in the file format sal_Int32 nDomainCount = maDomainAddresses.size(); bool bIsScatterChart = maSeriesChartTypeName == "com.sun.star.chart2.ScatterChartType"; - bool bIsBubbleChart = maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType"; + // A correlation circle plot carries its two dimension columns in a domain + // each, the same way a bubble chart carries its x and y values. + bool bHasTwoDomains = maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType" + || maSeriesChartTypeName == "com.sun.star.chart2.CorrelationCircleChartType"; bool bDeleteSeries = false; std::vector< DomainInfo > aDomainInfos; //different handling for different chart types necessary - if( bIsScatterChart || ( nDomainCount==1 && !bIsBubbleChart ) ) + if( bIsScatterChart || ( nDomainCount==1 && !bHasTwoDomains ) ) { DomainInfo aDomainInfo( u"values-x"_ustr, m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ; bool bCreateXValues = true; @@ -602,7 +607,7 @@ void SchXMLSeries2Context::endFastElement(sal_Int32 ) if( bCreateXValues ) aDomainInfos.push_back( aDomainInfo ); } - else if( bIsBubbleChart ) + else if( bHasTwoDomains ) { if( nDomainCount>1 ) { diff --git a/xmloff/source/chart/SchXMLTools.cxx b/xmloff/source/chart/SchXMLTools.cxx index 06faa64191fa..81316161b419 100644 --- a/xmloff/source/chart/SchXMLTools.cxx +++ b/xmloff/source/chart/SchXMLTools.cxx @@ -145,6 +145,7 @@ const SvXMLEnumMapEntry aXMLChartClassMap[] = { XML_HISTOGRAM, XML_CHART_CLASS_HISTOGRAM }, { XML_STOCK, XML_CHART_CLASS_STOCK }, { XML_BUBBLE, XML_CHART_CLASS_BUBBLE }, + { XML_CORRELATION_CIRCLE, XML_CHART_CLASS_CORRELATION_CIRCLE }, { XML_SURFACE, XML_CHART_CLASS_BAR }, //@todo change this if a surface chart is available { XML_ADD_IN, XML_CHART_CLASS_ADDIN }, { XML_TOKEN_INVALID, XML_CHART_CLASS_UNKNOWN } @@ -188,7 +189,9 @@ static const tMakeStringStringMap& lcl_getChartTypeNameMap() {"com.sun.star.chart.StockDiagram", "com.sun.star.chart2.CandleStickChartType"}, {"com.sun.star.chart.BubbleDiagram", - "com.sun.star.chart2.BubbleChartType"}}; + "com.sun.star.chart2.BubbleChartType"}, + {"com.sun.star.chart.CorrelationCircleDiagram", + "com.sun.star.chart2.CorrelationCircleChartType"}}; return g_aChartTypeNameMap; } @@ -248,6 +251,8 @@ OUString GetChartTypeByClassName( else if( IsXMLToken( rClassName, XML_BUBBLE )) aResultBuffer.append("Bubble"); + else if( IsXMLToken( rClassName, XML_CORRELATION_CIRCLE )) + aResultBuffer.append("CorrelationCircle"); else if( IsXMLToken( rClassName, XML_RADAR )) aResultBuffer.append("Net"); else if( IsXMLToken( rClassName, XML_FILLED_RADAR )) @@ -328,6 +333,8 @@ XMLTokenEnum getTokenByChartType( eResult = XML_SCATTER; else if ( aServiceName == u"Bubble" ) eResult = XML_BUBBLE; + else if ( aServiceName == u"CorrelationCircle" ) + eResult = XML_CORRELATION_CIRCLE; else if ( aServiceName == u"Net" ) eResult = XML_RADAR; else if ( aServiceName == u"FilledNet" ) diff --git a/xmloff/source/chart/SchXMLTools.hxx b/xmloff/source/chart/SchXMLTools.hxx index c7b091d40a78..94c080f0f724 100644 --- a/xmloff/source/chart/SchXMLTools.hxx +++ b/xmloff/source/chart/SchXMLTools.hxx @@ -63,6 +63,7 @@ namespace SchXMLTools XML_CHART_CLASS_HISTOGRAM, XML_CHART_CLASS_STOCK, XML_CHART_CLASS_BUBBLE, + XML_CHART_CLASS_CORRELATION_CIRCLE, XML_CHART_CLASS_ADDIN, XML_CHART_CLASS_UNKNOWN }; diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index f53014f9d0df..b40ff54681e7 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -548,6 +548,7 @@ namespace xmloff::token { TOKEN( "copy-styles", XML_COPY_STYLES ), TOKEN( "corner-radius", XML_CORNER_RADIUS ), TOKEN( "correct", XML_CORRECT ), + TOKEN( "correlation-circle", XML_CORRELATION_CIRCLE ), TOKEN( "cos", XML_COS ), TOKEN( "cosh", XML_COSH ), TOKEN( "cot", XML_COT ), diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 55e8e9168914..bc5dd85f814a 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -452,6 +452,7 @@ copy-results-only copy-styles corner-radius correct +correlation-circle cos cosh cot